// Aqsis
// Copyright 1997 - 2001, Paul C. Gregory
//
// Contact: pgregory@aqsis.org
//
// This library 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 library 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 library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


/** \file
		\brief Implement the majority of the RenderMan API functions.
		\author Paul C. Gregory (pgregory@aqsis.org)
		\todo: <b>Code Review</b>
*/

#include	<aqsis/aqsis.h>

#include	<fstream>
#include	<stdarg.h>
#include	<math.h>
#include	<stdio.h>
#include    <stdlib.h>

#include	<boost/filesystem/fstream.hpp>

#include	"imagebuffer.h"
#include	"lights.h"
#include	"renderer.h"
#include	"patch.h"
#include	"polygon.h"
#include	"nurbs.h"
#include	"quadrics.h"
#include	"teapot.h"
#include	"bunny.h"
#include	"shaders.h"
#include	"trimcurve.h"
#include	"genpoly.h"
#include	"points.h"
#include	"curves.h"
#include	"procedural.h"
#include	<aqsis/core/corecontext.h>
#include	<aqsis/riutil/ri2ricxx.h>
#include	<aqsis/riutil/ricxxutil.h>
#include	<aqsis/riutil/ricxx_filter.h>
#include	<aqsis/riutil/ribparser.h>
#include	<aqsis/riutil/risyms.h>
#include	<aqsis/riutil/ribwriter.h>
#include	<aqsis/util/file.h>
#include	<aqsis/util/logging.h>
#include	<aqsis/util/logging_streambufs.h>
#include	<aqsis/util/smartptr.h>
#include	<aqsis/tex/maketexture.h>
#include	"stats.h"
#include	<aqsis/math/random.h>
#include	"../../riutil/errorhandlerimpl.h"

#include	"subdivision2.h"
#include	"condition.h"

#include	"blobby.h"

#ifndef    AQSIS_SYSTEM_WIN32
#include        "unistd.h"
#else
#include	"io.h"
#endif /* AQSIS_SYSTEM_WIN32 */

// These are needed to allow calculation of the default paths
#ifdef AQSIS_SYSTEM_WIN32
  #include <windows.h>
  #ifdef _DEBUG
    #include <crtdbg.h>
extern "C" __declspec(dllimport) void report_refcounts();
#endif // _DEBUG
#endif // !AQSIS_SYSTEM_WIN32

#if defined(AQSIS_SYSTEM_MACOSX)
#include "Carbon/Carbon.h"
#endif

#include	<aqsis/ri/ri.h>

#include	<aqsis/util/sstring.h>


namespace Aqsis {

static RtBoolean ProcessPrimitiveVariables(CqSurface * pSurface,
										   const Ri::ParamList& pList);
RtVoid	CreateGPrim( const boost::shared_ptr<CqSurface>& pSurface );


//------------------------------------------------------------------------------
/// API for the core renderer
class RiCxxCore : public Ri::Renderer
{
	public:
		RiCxxCore(Ri::RendererServices& apiServices)
			: m_apiServices(apiServices),
			m_archiveCallback(0)
		{ }

        virtual RtVoid ArchiveRecord(RtConstToken type, const char* string)
		{
			if(m_archiveCallback)
				m_archiveCallback(const_cast<RtToken>(type),
								  const_cast<char*>("%s"), string);
		}

        // Code generator for autogenerated method declarations
        /*[[[cog
        from codegenutils import *

        riXml = parseXml(riXmlPath)

        for p in riXml.findall('Procedures/Procedure'):
            if p.findall('Rib'):
                decl = 'virtual %s;' % (riCxxMethodDecl(p),)
                cog.outl(wrapDecl(decl, 72, wrapIndent=20))
        ]]]*/
        virtual RtVoid Declare(RtConstString name, RtConstString declaration);
        virtual RtVoid FrameBegin(RtInt number);
        virtual RtVoid FrameEnd();
        virtual RtVoid WorldBegin();
        virtual RtVoid WorldEnd();
        virtual RtVoid IfBegin(RtConstString condition);
        virtual RtVoid ElseIf(RtConstString condition);
        virtual RtVoid Else();
        virtual RtVoid IfEnd();
        virtual RtVoid Format(RtInt xresolution, RtInt yresolution,
                            RtFloat pixelaspectratio);
        virtual RtVoid FrameAspectRatio(RtFloat frameratio);
        virtual RtVoid ScreenWindow(RtFloat left, RtFloat right, RtFloat bottom,
                            RtFloat top);
        virtual RtVoid CropWindow(RtFloat xmin, RtFloat xmax, RtFloat ymin,
                            RtFloat ymax);
        virtual RtVoid Projection(RtConstToken name, const ParamList& pList);
        virtual RtVoid Clipping(RtFloat cnear, RtFloat cfar);
        virtual RtVoid ClippingPlane(RtFloat x, RtFloat y, RtFloat z,
                            RtFloat nx, RtFloat ny, RtFloat nz);
        virtual RtVoid DepthOfField(RtFloat fstop, RtFloat focallength,
                            RtFloat focaldistance);
        virtual RtVoid Shutter(RtFloat opentime, RtFloat closetime);
        virtual RtVoid PixelVariance(RtFloat variance);
        virtual RtVoid PixelSamples(RtFloat xsamples, RtFloat ysamples);
        virtual RtVoid PixelFilter(RtFilterFunc function, RtFloat xwidth,
                            RtFloat ywidth);
        virtual RtVoid Exposure(RtFloat gain, RtFloat gamma);
        virtual RtVoid Imager(RtConstToken name, const ParamList& pList);
        virtual RtVoid Quantize(RtConstToken type, RtInt one, RtInt min,
                            RtInt max, RtFloat ditheramplitude);
        virtual RtVoid Display(RtConstToken name, RtConstToken type,
                            RtConstToken mode, const ParamList& pList);
        virtual RtVoid Hider(RtConstToken name, const ParamList& pList);
        virtual RtVoid ColorSamples(const FloatArray& nRGB,
                            const FloatArray& RGBn);
        virtual RtVoid RelativeDetail(RtFloat relativedetail);
        virtual RtVoid Option(RtConstToken name, const ParamList& pList);
        virtual RtVoid AttributeBegin();
        virtual RtVoid AttributeEnd();
        virtual RtVoid Color(RtConstColor Cq);
        virtual RtVoid Opacity(RtConstColor Os);
        virtual RtVoid TextureCoordinates(RtFloat s1, RtFloat t1, RtFloat s2,
                            RtFloat t2, RtFloat s3, RtFloat t3, RtFloat s4,
                            RtFloat t4);
        virtual RtVoid LightSource(RtConstToken shadername, RtConstToken name,
                            const ParamList& pList);
        virtual RtVoid AreaLightSource(RtConstToken shadername,
                            RtConstToken name, const ParamList& pList);
        virtual RtVoid Illuminate(RtConstToken name, RtBoolean onoff);
        virtual RtVoid Surface(RtConstToken name, const ParamList& pList);
        virtual RtVoid Displacement(RtConstToken name, const ParamList& pList);
        virtual RtVoid Atmosphere(RtConstToken name, const ParamList& pList);
        virtual RtVoid Interior(RtConstToken name, const ParamList& pList);
        virtual RtVoid Exterior(RtConstToken name, const ParamList& pList);
        virtual RtVoid ShaderLayer(RtConstToken type, RtConstToken name,
                            RtConstToken layername, const ParamList& pList);
        virtual RtVoid ConnectShaderLayers(RtConstToken type,
                            RtConstToken layer1, RtConstToken variable1,
                            RtConstToken layer2, RtConstToken variable2);
        virtual RtVoid ShadingRate(RtFloat size);
        virtual RtVoid ShadingInterpolation(RtConstToken type);
        virtual RtVoid Matte(RtBoolean onoff);
        virtual RtVoid Bound(RtConstBound bound);
        virtual RtVoid Detail(RtConstBound bound);
        virtual RtVoid DetailRange(RtFloat offlow, RtFloat onlow,
                            RtFloat onhigh, RtFloat offhigh);
        virtual RtVoid GeometricApproximation(RtConstToken type,
                            RtFloat value);
        virtual RtVoid Orientation(RtConstToken orientation);
        virtual RtVoid ReverseOrientation();
        virtual RtVoid Sides(RtInt nsides);
        virtual RtVoid Identity();
        virtual RtVoid Transform(RtConstMatrix transform);
        virtual RtVoid ConcatTransform(RtConstMatrix transform);
        virtual RtVoid Perspective(RtFloat fov);
        virtual RtVoid Translate(RtFloat dx, RtFloat dy, RtFloat dz);
        virtual RtVoid Rotate(RtFloat angle, RtFloat dx, RtFloat dy,
                            RtFloat dz);
        virtual RtVoid Scale(RtFloat sx, RtFloat sy, RtFloat sz);
        virtual RtVoid Skew(RtFloat angle, RtFloat dx1, RtFloat dy1,
                            RtFloat dz1, RtFloat dx2, RtFloat dy2,
                            RtFloat dz2);
        virtual RtVoid CoordinateSystem(RtConstToken space);
        virtual RtVoid CoordSysTransform(RtConstToken space);
        virtual RtVoid TransformBegin();
        virtual RtVoid TransformEnd();
        virtual RtVoid Resource(RtConstToken handle, RtConstToken type,
                            const ParamList& pList);
        virtual RtVoid ResourceBegin();
        virtual RtVoid ResourceEnd();
        virtual RtVoid Attribute(RtConstToken name, const ParamList& pList);
        virtual RtVoid Polygon(const ParamList& pList);
        virtual RtVoid GeneralPolygon(const IntArray& nverts,
                            const ParamList& pList);
        virtual RtVoid PointsPolygons(const IntArray& nverts,
                            const IntArray& verts, const ParamList& pList);
        virtual RtVoid PointsGeneralPolygons(const IntArray& nloops,
                            const IntArray& nverts, const IntArray& verts,
                            const ParamList& pList);
        virtual RtVoid Basis(RtConstBasis ubasis, RtInt ustep,
                            RtConstBasis vbasis, RtInt vstep);
        virtual RtVoid Patch(RtConstToken type, const ParamList& pList);
        virtual RtVoid PatchMesh(RtConstToken type, RtInt nu,
                            RtConstToken uwrap, RtInt nv, RtConstToken vwrap,
                            const ParamList& pList);
        virtual RtVoid NuPatch(RtInt nu, RtInt uorder, const FloatArray& uknot,
                            RtFloat umin, RtFloat umax, RtInt nv, RtInt vorder,
                            const FloatArray& vknot, RtFloat vmin, RtFloat vmax,
                            const ParamList& pList);
        virtual RtVoid TrimCurve(const IntArray& ncurves, const IntArray& order,
                            const FloatArray& knot, const FloatArray& min,
                            const FloatArray& max, const IntArray& n,
                            const FloatArray& u, const FloatArray& v,
                            const FloatArray& w);
        virtual RtVoid SubdivisionMesh(RtConstToken scheme,
                            const IntArray& nvertices, const IntArray& vertices,
                            const TokenArray& tags, const IntArray& nargs,
                            const IntArray& intargs,
                            const FloatArray& floatargs,
                            const ParamList& pList);
        virtual RtVoid Sphere(RtFloat radius, RtFloat zmin, RtFloat zmax,
                            RtFloat thetamax, const ParamList& pList);
        virtual RtVoid Cone(RtFloat height, RtFloat radius, RtFloat thetamax,
                            const ParamList& pList);
        virtual RtVoid Cylinder(RtFloat radius, RtFloat zmin, RtFloat zmax,
                            RtFloat thetamax, const ParamList& pList);
        virtual RtVoid Hyperboloid(RtConstPoint point1, RtConstPoint point2,
                            RtFloat thetamax, const ParamList& pList);
        virtual RtVoid Paraboloid(RtFloat rmax, RtFloat zmin, RtFloat zmax,
                            RtFloat thetamax, const ParamList& pList);
        virtual RtVoid Disk(RtFloat height, RtFloat radius, RtFloat thetamax,
                            const ParamList& pList);
        virtual RtVoid Torus(RtFloat majorrad, RtFloat minorrad, RtFloat phimin,
                            RtFloat phimax, RtFloat thetamax,
                            const ParamList& pList);
        virtual RtVoid Points(const ParamList& pList);
        virtual RtVoid Curves(RtConstToken type, const IntArray& nvertices,
                            RtConstToken wrap, const ParamList& pList);
        virtual RtVoid Blobby(RtInt nleaf, const IntArray& code,
                            const FloatArray& floats, const TokenArray& strings,
                            const ParamList& pList);
        virtual RtVoid Procedural(RtPointer data, RtConstBound bound,
                            RtProcSubdivFunc refineproc,
                            RtProcFreeFunc freeproc);
        virtual RtVoid Geometry(RtConstToken type, const ParamList& pList);
        virtual RtVoid SolidBegin(RtConstToken type);
        virtual RtVoid SolidEnd();
        virtual RtVoid ObjectBegin(RtConstToken name);
        virtual RtVoid ObjectEnd();
        virtual RtVoid ObjectInstance(RtConstToken name);
        virtual RtVoid MotionBegin(const FloatArray& times);
        virtual RtVoid MotionEnd();
        virtual RtVoid MakeTexture(RtConstString imagefile,
                            RtConstString texturefile, RtConstToken swrap,
                            RtConstToken twrap, RtFilterFunc filterfunc,
                            RtFloat swidth, RtFloat twidth,
                            const ParamList& pList);
        virtual RtVoid MakeLatLongEnvironment(RtConstString imagefile,
                            RtConstString reflfile, RtFilterFunc filterfunc,
                            RtFloat swidth, RtFloat twidth,
                            const ParamList& pList);
        virtual RtVoid MakeCubeFaceEnvironment(RtConstString px,
                            RtConstString nx, RtConstString py,
                            RtConstString ny, RtConstString pz,
                            RtConstString nz, RtConstString reflfile,
                            RtFloat fov, RtFilterFunc filterfunc,
                            RtFloat swidth, RtFloat twidth,
                            const ParamList& pList);
        virtual RtVoid MakeShadow(RtConstString picfile,
                            RtConstString shadowfile, const ParamList& pList);
        virtual RtVoid MakeOcclusion(const StringArray& picfiles,
                            RtConstString shadowfile, const ParamList& pList);
        virtual RtVoid ErrorHandler(RtErrorFunc handler);
        virtual RtVoid ReadArchive(RtConstToken name,
                            RtArchiveCallback callback,
                            const ParamList& pList);
        virtual RtVoid ArchiveBegin(RtConstToken name, const ParamList& pList);
        virtual RtVoid ArchiveEnd();
		//[[[end]]]

	private:
		Ri::ErrorHandler& errorHandler()
		{
			return m_apiServices.errorHandler();
		}

		Ri::RendererServices& m_apiServices;
		RtArchiveCallback m_archiveCallback;
};

//------------------------------------------------------------------------------
// Set arguments to a shader.
void setShaderArguments(const boost::shared_ptr<IqShader>& pShader,
						const Ri::ParamList& pList)
{
	for(size_t i = 0; i < pList.size(); ++i)
	{
		EqVariableClass iclass;
		EqVariableType type;
		typeSpecToEqTypes(&iclass, &type, pList[i].spec());
		pShader->SetArgument(pList[i].name(), type, "", const_cast<void*>(pList[i].data()));
	}
}


//----------------------------------------------------------------------
// CreateGPrim
// Helper function to build a GPrim from any boost::shared_ptr<> type..
template<class T>
inline
RtVoid	CreateGPrim( const boost::shared_ptr<T>& pSurface )
{
	CreateGPrim( boost::static_pointer_cast<CqSurface,T>( pSurface ) );
}


//----------------------------------------------------------------------
// Declare a new variable to be recognised by the system.
//
RtVoid RiCxxCore::Declare(RtConstString name, RtConstString declaration)
{
	if(declaration)
		QGetRenderContext()->tokenDict().declare(name, declaration);
	else // declaration is allowed to be RI_NULL
		QGetRenderContext()->tokenDict().declare(name, Ri::TypeSpec());
}


//----------------------------------------------------------------------
// SetDefaultRiOptions
// Set some Default Options.
//
void SetDefaultRiOptions()
{
	// Get the root path for the aqsis installation.
	boost::filesystem::path rootPath;
#ifdef AQSIS_SYSTEM_WIN32

	char acPath[256];
	char root[256];
	if( GetModuleFileName( NULL, acPath, 256 ) != 0)
	{
		// guaranteed file name of at least one character after path
		*( strrchr( acPath, '\\' ) ) = '\0';
		std::string      stracPath(acPath);
		_fullpath(root,&stracPath[0],256);
	}
	rootPath = root;
#elif AQSIS_SYSTEM_MACOSX

	CFURLRef pluginRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
	CFStringRef macPath = CFURLCopyFileSystemPath(pluginRef, kCFURLPOSIXPathStyle);
	const char *pathPtr = CFStringGetCStringPtr(macPath, CFStringGetSystemEncoding());
	rootPath = pathPtr;
	CFRelease(macPath);
	CFRelease(pluginRef);
#else
	// Minty: Need to work out the executable path here.
	rootPath = AQSIS_XSTR(DEFAULT_RC_PATH);
#endif

	// Read in the system configuration file.
	boost::filesystem::path systemRcPath = rootPath / AQSIS_XSTR(AQSIS_MAIN_CONFIG_NAME);
	std::ifstream rcFile(native(systemRcPath).c_str(), std::ios::binary);
	if(rcFile)
	{
		Aqsis::log() << info
			<< "Reading system config \"" << systemRcPath << "\"\n";
		cxxRenderContext()->parseRib(rcFile, native(systemRcPath).c_str());
		rcFile.close();
	}
	else
	{
		Aqsis::log() << error
			<< "Could not open system config (" << systemRcPath << ")\n";
	}

	// Read in the user-specific config in $HOME/.aqsisrc 
	if(const char* homePath = getenv("HOME"))
	{
		boost::filesystem::path homeRcPath = homePath;
		homeRcPath /= ".aqsisrc";

		std::ifstream rcFile(native(homeRcPath).c_str(), std::ios::binary);
		if(rcFile)
		{
			Aqsis::log() << info << "Reading user config \"" << homeRcPath << "\"\n";
			cxxRenderContext()->parseRib(rcFile, native(homeRcPath).c_str());
		}
		else
		{
			boost::filesystem::path homeRcPath2 = homePath;
			homeRcPath2 /= "_aqsisrc";
		
			std::ifstream rcFile(native(homeRcPath2).c_str(), std::ios::binary);
			if(rcFile)
			{
				Aqsis::log() << info << "Reading user config \"" << homeRcPath2 << "\"\n";
				cxxRenderContext()->parseRib(rcFile, native(homeRcPath2).c_str());
			}
			else
			{
				Aqsis::log() << info
					<< "Could not open user config \"" << homeRcPath << "\" or \"" << homeRcPath2 << "\"\n";
			}
		}
	}
	else
	{
		Aqsis::log() << info
			<< "Environment variable HOME not set (skipping user config).\n";
	}

	// Read the config file for the current directory.
	std::string currentRcPath = ".aqsisrc";
	rcFile.open(currentRcPath.c_str(), std::ios::binary);
	if(rcFile)
	{
		cxxRenderContext()->parseRib(rcFile, currentRcPath.c_str());
		rcFile.close();
		Aqsis::log() << info << "Reading project config \"" << currentRcPath << "\"\n";
	}
	else
	{
		std::string currentRcPath2 = "_aqsisrc";
		rcFile.open(currentRcPath2.c_str(), std::ios::binary);

		if(rcFile)
		{
			cxxRenderContext()->parseRib(rcFile, currentRcPath.c_str());
			rcFile.close();
			Aqsis::log() << info << "Reading project config \"" << currentRcPath2 << "\"\n";
		}
		else
		{
			Aqsis::log() << info
				<< "Could not open project config \"" << currentRcPath << "\" or \"" << currentRcPath2 << "\"\n";
		}
	}

	// Set options from various environment variables.
	const char* popt[ 1 ];
	if(getenv("AQSIS_SHADER_PATH"))
	{
		popt[0] = getenv("AQSIS_SHADER_PATH");
		Aqsis::log() << info << "Applying AQSIS_SHADER_PATH (" << popt[0] << ")" << std::endl;
		RiOption( tokenCast("searchpath"), "shader", &popt, RI_NULL );
	}
	else
	{
		Aqsis::log() << info << "AQSIS_SHADER_PATH not set" << std::endl;
	}

	if(getenv("AQSIS_ARCHIVE_PATH"))
	{
		popt[0] = getenv("AQSIS_ARCHIVE_PATH");
		Aqsis::log() << info << "Applying AQSIS_ARCHIVE_PATH (" << popt[0] << ")" << std::endl;
		RiOption( tokenCast("searchpath"), "archive", &popt, RI_NULL );
	}
	else
	{
		Aqsis::log() << info << "AQSIS_ARCHIVE_PATH not set" << std::endl;
	}

	if(getenv("AQSIS_TEXTURE_PATH"))
	{
		popt[0] = getenv("AQSIS_TEXTURE_PATH");
		Aqsis::log() << info << "Applying AQSIS_TEXTURE_PATH (" << popt[0] << ")" << std::endl;
		RiOption( tokenCast("searchpath"), "texture", &popt, RI_NULL );
	}
	else
	{
		Aqsis::log() << info << "AQSIS_TEXTURE_PATH not set" << std::endl;
	}

	if(getenv("AQSIS_DISPLAY_PATH"))
	{
		popt[0] = getenv("AQSIS_DISPLAY_PATH");
		Aqsis::log() << info << "Applying AQSIS_DISPLAY_PATH (" << popt[0] << ")" << std::endl;
		RiOption( tokenCast("searchpath"), "display", &popt, RI_NULL );
	}
	else
	{
		Aqsis::log() << info << "AQSIS_DISPLAY_PATH not set" << std::endl;
	}

	if(getenv("AQSIS_PROCEDURAL_PATH"))
	{
		popt[0] = getenv("AQSIS_PROCEDURAL_PATH");
		Aqsis::log() << info << "Applying AQSIS_PROCEDURAL_PATH (" << popt[0] << ")" << std::endl;
		RiOption( tokenCast("searchpath"), "procedural", &popt, RI_NULL );
	}
	else
	{
		Aqsis::log() << info << "AQSIS_PROCEDURAL_PATH not set" << std::endl;
	}

	// Setup a default Display
	Aqsis::log() << info << "Setting up default display: Display \"ri.pic\" \"file\" \"rgba\"" << std::endl;
	RiDisplay( tokenCast("ri.pic"), tokenCast("file"), tokenCast("rgba"), NULL );
}

//----------------------------------------------------------------------
// Begin an individual frame, options are saved at this point.
//
RtVoid RiCxxCore::FrameBegin(RtInt number)
{
	// Initialise the statistics variables. If the RIB doesn't contain
	// a Frame-block the initialisation was previously done in CqStats::Initilise()
	// which has to be called before a rendering session.
	QGetRenderContext() ->Stats().InitialiseFrame();
	// Start the timer. Note: The corresponding call of StopFrameTimer() is
	// done in WorldEnd (!) not FrameEnd since it can happen that there is
	// not FrameEnd (and usually there's not much between WorldEnd and FrameEnd).
	//QGetRenderContext() ->Stats().StartFrameTimer();
	AQSIS_TIMER_START(Frame);

	QGetRenderContext() ->BeginFrameModeBlock();
	QGetRenderContext() ->SetCurrentFrame( number );
	CqCSGTreeNode::SetRequired( false );

	QGetRenderContext() ->Stats().InitialiseFrame();

	QGetRenderContext()->clippingVolume().clear();

	CqRandom().Reseed('a'+'q'+'s'+'i'+'s');
}


//----------------------------------------------------------------------
// End the rendering of an individual frame, options are restored.
//
RtVoid RiCxxCore::FrameEnd()
{
	QGetRenderContext() ->EndFrameModeBlock();
	QGetRenderContext() ->ClearDisplayRequests();
}

//----------------------------------------------------------------------
// Start the information for the world, options are now frozen.  The world-to-camera
// transformation is set to the current transformation, and current is set to identity.
//
RtVoid RiCxxCore::WorldBegin()
{
	// Start the frame timer (just in case there was no FrameBegin block. If there
	// was, nothing happens)
	//QGetRenderContext() ->Stats().StartFrameTimer();
	AQSIS_TIMER_START(Frame);
	AQSIS_TIMER_START(Parse);

	// Now that the options have all been set, setup any undefined camera parameters.
	const TqInt* pCameraOpts = QGetRenderContext()->poptCurrent()->GetIntegerOption("System", "CameraFlags");
	TqInt cameraOpts = 0;
	if(pCameraOpts != NULL)
		cameraOpts = pCameraOpts[0];
	if ( (cameraOpts & CameraFARSet) == 0 )
	{
		// Derive the FAR from the resolution and pixel aspect ratio.
		RtFloat PAR = QGetRenderContext() ->poptCurrent()->GetFloatOption( "System", "PixelAspectRatio" ) [ 0 ];
		RtFloat resH = QGetRenderContext() ->poptCurrent()->GetIntegerOption( "System", "Resolution" ) [ 0 ];
		RtFloat resV = QGetRenderContext() ->poptCurrent()->GetIntegerOption( "System", "Resolution" ) [ 1 ];
		QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "FrameAspectRatio" ) [ 0 ] = ( resH * PAR ) / resV ;
	}

	if ( ( cameraOpts & CameraScreenWindowSet) == 0 )
	{
		RtFloat fFAR = QGetRenderContext() ->poptCurrent()->GetFloatOption( "System", "FrameAspectRatio" ) [ 0 ];

		if ( fFAR >= 1.0 )
		{
			QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "ScreenWindow" ) [ 0 ] = -fFAR ;
			QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "ScreenWindow" ) [ 1 ] = + fFAR ;
			QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "ScreenWindow" ) [ 2 ] = + 1 ;
			QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "ScreenWindow" ) [ 3 ] = -1 ;
		}
		else
		{
			QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "ScreenWindow" ) [ 0 ] = -1 ;
			QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "ScreenWindow" ) [ 1 ] = + 1 ;
			QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "ScreenWindow" ) [ 2 ] = + 1.0 / fFAR ;
			QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "ScreenWindow" ) [ 3 ] = -1.0 / fFAR ;
		}
	}

	// Set the world to camera transformation matrix to the current matrix.

	CqTransformPtr current( QGetRenderContext() ->ptransCurrent() );
	QGetRenderContext() ->SetCameraTransform( current );
	QGetRenderContext() ->BeginWorldModeBlock();
	// Set the camera transformation for shadow maps in the sampler cache.
	/// \todo What is the correct coordinate system to use here? "current"? "shader"?
	CqMatrix currToWorldMat;
	QGetRenderContext()->matSpaceToSpace("current", "world", NULL, NULL, 0, currToWorldMat);
	QGetRenderContext()->textureCache().setCurrToWorldMatrix(currToWorldMat);

	// Reset the current transformation to identity, this now represents the object-->world transform.
	QGetRenderContext() ->ptransSetTime( CqMatrix() );

	// Store the initial object transformation
	CqTransformPtr newTrans( new CqTransform() );
	QGetRenderContext()->SetDefObjTransform( newTrans );

	// If rendering a depth buffer, check that the filter is "box" 1x1, warn if not.
	TqInt iMode = QGetRenderContext() ->poptCurrent()->GetIntegerOption( "System", "DisplayMode" ) [ 0 ];
	if( iMode & DMode_Z )
	{
		RtFilterFunc filter = QGetRenderContext() ->poptCurrent()->funcFilter();
		TqFloat xwidth = QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "FilterWidth" ) [ 0 ];
		TqFloat ywidth = QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "FilterWidth" ) [ 1 ];
		if( filter != RiBoxFilter || xwidth != 1 || ywidth != 1)
			Aqsis::log() << warning << "When rendering a Z buffer the filter mode should be \"box\" with a width of 1x1" << std::endl;
	}

	QGetRenderContext()->SetWorldBegin();
	
	// Ensure that the camera and projection matrices are initialised.
	// This is also done in CqRenderer::RenderWorld, but needs to be 
	// done here also in case we're not running in 'multipass' mode, in 
	// which case the primitives all 'fast track' into the pipeline and
	// therefore rely on information setup here.
	QGetRenderContext()->poptWriteCurrent()->InitialiseCamera();
	// initialiseCropWindow() currently needs to be called befure SetImage() (ugh)
	QGetRenderContext()->initialiseCropWindow();
	QGetRenderContext()->pImage()->SetImage();

	CqRandom().Reseed('a'+'q'+'s'+'i'+'s');
}


//----------------------------------------------------------------------
// End the specifying of world data, options are released.
//

RtVoid RiCxxCore::WorldEnd()
{
	QGetRenderContext()->RenderAutoShadows();

	bool fFailed = false;

	// Stop the parsing counter
	AQSIS_TIMER_STOP(Parse);


	QGetRenderContext() -> Stats().PrintInfo();

	const TqInt* poptGridSize = QGetRenderContext() ->poptCurrent()->GetIntegerOption( "limits", "gridsize" );
	if( NULL != poptGridSize )
		QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "SqrtGridSize" )[0] = sqrt( static_cast<float>(poptGridSize[0]) );

	// Finalise the raytracer database now that all primitives are in.
	if(QGetRenderContext()->pRaytracer())
		QGetRenderContext()->pRaytracer()->Finalise();

	// Render the world
	try
	{
		QGetRenderContext() ->RenderWorld();
	}
	catch ( CqString strError )
	{
		Aqsis::log() << error << strError.c_str() << std::endl;
		fFailed = true;
	}

	// Remove all cached textures.
	QGetRenderContext()->textureCache().flush();

	// Clear out point cloud caches, etc.
	clearShaderSystemCaches();

	// Delete the world context
	QGetRenderContext() ->EndWorldModeBlock();

	// Stop the frame timer
	AQSIS_TIMER_STOP(Frame);

	if ( !fFailed )
	{
		// Get the verbosity level from the options..
		TqInt verbosity = 0;
		const TqInt* poptEndofframe = QGetRenderContext() ->poptCurrent()->GetIntegerOption( "statistics", "endofframe" );
		if ( poptEndofframe != 0 )
			verbosity = poptEndofframe[ 0 ];

		// ..and print the statistics.
		QGetRenderContext() ->Stats().PrintStats( verbosity );
	}

	QGetRenderContext()->SetWorldBegin(false);
}


//----------------------------------------------------------------------
// Specify the setup of the final image.
//
RtVoid RiCxxCore::Format(RtInt xresolution, RtInt yresolution, RtFloat pixelaspectratio)
{
	QGetRenderContext() ->poptWriteCurrent()->GetIntegerOptionWrite( "System", "Resolution" ) [ 0 ] = xresolution ;
	QGetRenderContext() ->poptWriteCurrent()->GetIntegerOptionWrite( "System", "Resolution" ) [ 1 ] = yresolution ;
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "PixelAspectRatio" ) [ 0 ] = ( pixelaspectratio < 0.0 ) ? 1.0 : pixelaspectratio ;
}


//----------------------------------------------------------------------
// Set the aspect ratio of the frame irrespective of the display setup.
//
RtVoid RiCxxCore::FrameAspectRatio(RtFloat frameratio)
{
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "FrameAspectRatio" ) [ 0 ] = frameratio ;

	// Inform the system that RiFrameAspectRatio has been called, as this takes priority.
	QGetRenderContext()->poptWriteCurrent()->GetIntegerOptionWrite("System", "CameraFlags")[0] |= CameraFARSet;
}


//----------------------------------------------------------------------
// Set the resolution of the screen window in the image plane specified in the screen
// coordinate system.
//
RtVoid RiCxxCore::ScreenWindow(RtFloat left, RtFloat right, RtFloat bottom, RtFloat top)
{
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "ScreenWindow" ) [ 0 ] = left ;
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "ScreenWindow" ) [ 1 ] = right ;
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "ScreenWindow" ) [ 2 ] = top ;
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "ScreenWindow" ) [ 3 ] = bottom ;

	// Inform the system that RiScreenWindow has been called, as this takes priority.
	QGetRenderContext()->poptWriteCurrent()->GetIntegerOptionWrite("System", "CameraFlags")[0] |= CameraScreenWindowSet;
}


//----------------------------------------------------------------------
// Set the position and size of the crop window specified in fractions of the raster
// window.
//
RtVoid RiCxxCore::CropWindow(RtFloat xmin, RtFloat xmax, RtFloat ymin, RtFloat ymax)
{
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "CropWindow" ) [ 0 ] = xmin ;
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "CropWindow" ) [ 1 ] = xmax ;
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "CropWindow" ) [ 2 ] = ymin ;
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "CropWindow" ) [ 3 ] = ymax ;
}


//----------------------------------------------------------------------
// List mode version of above.
//
RtVoid RiCxxCore::Projection(RtConstToken name, const ParamList& pList)
{
	if(NULL != name)
	{
		if ( strcmp( name, RI_PERSPECTIVE ) == 0 )
			QGetRenderContext() ->poptWriteCurrent()->GetIntegerOptionWrite( "System", "Projection" ) [ 0 ] = ProjectionPerspective ;
		else if	( strcmp( name, RI_ORTHOGRAPHIC ) == 0 )
			QGetRenderContext() ->poptWriteCurrent()->GetIntegerOptionWrite( "System", "Projection" ) [ 0 ] = ProjectionOrthographic ;
		else if( strlen( name ) == 0 || name == RI_NULL )
			QGetRenderContext() ->poptWriteCurrent()->GetIntegerOptionWrite( "System", "Projection" ) [ 0 ] = ProjectionNone ;
		else
		{
			Aqsis::log() << error << "RiProjection: Invalid projection: \"" << name << "\"" << std::endl;
			return ;
		}

		int fovIdx = pList.find(Ri::TypeSpec(Ri::TypeSpec::Float), "fov");
		if(fovIdx >= 0)
			QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "FOV" ) [ 0 ] = pList[fovIdx].floatData()[0];
	}
	// TODO: need to get the current transformation so that it can be added to the screen transformation.
	QGetRenderContext() ->SetpreProjectionTransform( QGetRenderContext() ->ptransCurrent() );
	QGetRenderContext() ->ptransSetTime( CqMatrix() );
}


//----------------------------------------------------------------------
// Set the near and far clipping planes specified as distances from the camera.
//
RtVoid RiCxxCore::Clipping(RtFloat cnear, RtFloat cfar)
{
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "Clipping" ) [ 0 ] = cnear;
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "Clipping" ) [ 1 ] = cfar;
}


//----------------------------------------------------------------------
// Specify the parameters which affect focal blur of the camera.
//
RtVoid RiCxxCore::DepthOfField(RtFloat fstop, RtFloat focallength, RtFloat focaldistance)
{
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "DepthOfField" ) [ 0 ] = fstop ;
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "DepthOfField" ) [ 1 ] = focallength ;
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "DepthOfField" ) [ 2 ] = focaldistance ;
}


//----------------------------------------------------------------------
//	Set the times at which the shutter opens and closes, used for motion blur.
//
RtVoid RiCxxCore::Shutter(RtFloat opentime, RtFloat closetime)
{
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "Shutter" ) [ 0 ] = opentime;
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "Shutter" ) [ 1 ] = closetime;
}


//----------------------------------------------------------------------
// Set the upper bound on the variance from the true pixel color by the pixel filter
// function.
//
RtVoid RiCxxCore::PixelVariance(RtFloat variance)
{
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "PixelVariance" ) [ 0 ] = variance ;
}


//----------------------------------------------------------------------
// Set the number of samples per pixel for the hidden surface function.
//
RtVoid RiCxxCore::PixelSamples(RtFloat xsamples, RtFloat ysamples)
{
	QGetRenderContext() ->poptWriteCurrent()->GetIntegerOptionWrite( "System", "PixelSamples" ) [ 0 ] = static_cast<TqInt>( xsamples ) ;
	QGetRenderContext() ->poptWriteCurrent()->GetIntegerOptionWrite( "System", "PixelSamples" ) [ 1 ] = static_cast<TqInt>( ysamples ) ;
}


//----------------------------------------------------------------------
// Set the function used to generate a final pixel value from supersampled values.
//
RtVoid RiCxxCore::PixelFilter(RtFilterFunc function, RtFloat xwidth, RtFloat ywidth)
{
	QGetRenderContext() ->poptWriteCurrent()->SetfuncFilter( function );
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "FilterWidth" ) [ 0 ] = xwidth ;
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "FilterWidth" ) [ 1 ] = ywidth ;
}


//----------------------------------------------------------------------
//	Set the values of the exposure color modification function.
//
RtVoid RiCxxCore::Exposure(RtFloat gain, RtFloat gamma)
{
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "Exposure" ) [ 0 ] = gain ;
	QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "Exposure" ) [ 1 ] = gamma ;
}


//----------------------------------------------------------------------
// Specify a prepocessing imager shader.
//
RtVoid RiCxxCore::Imager(RtConstToken name, const ParamList& pList)
{
	// Find the shader.
	boost::shared_ptr<IqShader> pshadImager = QGetRenderContext()->CreateShader( name, Type_Imager );

	if ( pshadImager )
	{
		QGetRenderContext()->poptWriteCurrent()->SetpshadImager( pshadImager );
		setShaderArguments(pshadImager, pList);
		const TqInt* pMultipass = QGetRenderContext()->poptCurrent()->GetIntegerOption("Render", "multipass");
		if(pMultipass && !pMultipass[0])
			pshadImager->PrepareShaderForUse();
	}
}


//----------------------------------------------------------------------
// Specify the color quantization parameters.
//
RtVoid RiCxxCore::Quantize(RtConstToken type, RtInt one, RtInt min, RtInt max, RtFloat ditheramplitude)
{
	if ( strcmp( type, "rgba" ) == 0 )
	{
		TqFloat* pColorQuantize = QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "Quantize", "Color" );
		pColorQuantize [ 0 ] = static_cast<TqFloat>( one );
		pColorQuantize [ 1 ] = static_cast<TqFloat>( min );
		pColorQuantize [ 2 ] = static_cast<TqFloat>( max );
		pColorQuantize [ 3 ] = static_cast<TqFloat>( ditheramplitude );
	}
	else if ( strcmp( type, "z" ) == 0 )
	{
		TqFloat* pDepthQuantize = QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "Quantize", "Depth" );
		pDepthQuantize [ 0 ] = static_cast<TqFloat>( one );
		pDepthQuantize [ 1 ] = static_cast<TqFloat>( min );
		pDepthQuantize [ 2 ] = static_cast<TqFloat>( max );
		pDepthQuantize [ 3 ] = static_cast<TqFloat>( ditheramplitude );
	}
	else
	{
		TqFloat* quantOpt = QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite("Quantize", type, 4);
		quantOpt[0] = static_cast<TqFloat>( one );
		quantOpt[1] = static_cast<TqFloat>( min );
		quantOpt[2] = static_cast<TqFloat>( max );
		quantOpt[3] = static_cast<TqFloat>( ditheramplitude );
	}
}


//----------------------------------------------------------------------
// Set the final output name and type.
//
RtVoid RiCxxCore::Display(RtConstToken name, RtConstToken type, RtConstToken mode, const ParamList& pList)
{
	CqString strName( name );
	CqString strType( type );

	QGetRenderContext() ->poptWriteCurrent()->GetStringOptionWrite( "System", "DisplayName" ) [ 0 ] = strName.c_str() ;
	QGetRenderContext() ->poptWriteCurrent()->GetStringOptionWrite( "System", "DisplayType" ) [ 0 ] = strType.c_str() ;

	// Append the display mode to the current setting.
	TqInt eValue = 0;
	TqInt index = 0;
	TqInt dataOffset = 0;
	TqInt dataSize = 0;
	std::string dataName = mode;
	if ( strncmp( mode, RI_RGB, strlen(RI_RGB) ) == 0 )
	{
		eValue |= DMode_RGB;
		dataSize += 3;
		index += strlen( RI_RGB );
	}
	if ( strncmp( &mode[index], RI_A, strlen( RI_A ) ) == 0 )
	{
		eValue |= DMode_A;
		dataSize += 1;
		index += strlen( RI_A );
	}
	if ( strncmp( &mode[index], RI_Z, strlen( RI_Z ) ) == 0 )
	{
		eValue |= DMode_Z;
		dataSize += 1;
		index += strlen( RI_Z );
	}

	// Special case test.
	if(strncmp(&mode[index], "depth", strlen("depth") ) == 0 )
	{
		dataSize = 1;
		/// \todo This shouldn't be a constant.
		dataOffset = 6;
	}
	// If none of the standard "rgbaz" strings match, then it is an alternative 'arbitrary output variable'
	else if( eValue == 0 )
	{
		dataOffset = QGetRenderContext()->RegisterOutputData( mode );
		dataSize = QGetRenderContext()->OutputDataSamples( mode );
		// Strip off any inline-declared types from dataName
		QGetRenderContext()->tokenDict().lookup(mode, &dataName);
	}

	// Check if the display request is valid.
	if(dataOffset >= 0 && dataSize >0)
	{
		// Gather the additional arguments into a map to pass through to the
		// manager.
		//
		// TODO: Just pass the pList directly!
		std::map<std::string, void*> mapOfArguments;
		for(size_t i = 0; i < pList.size(); ++i)
			mapOfArguments[ tokenString(pList[i]) ] = const_cast<void*>(pList[i].data());

		// Check if the request is to add a display driver.
		if ( strName[ 0 ] == '+' )
		{
			TqInt iMode = QGetRenderContext() ->poptCurrent()->GetIntegerOption( "System", "DisplayMode" ) [ 0 ] | eValue;
			QGetRenderContext() ->poptWriteCurrent()->GetIntegerOptionWrite( "System", "DisplayMode" ) [ 0 ] = iMode;
			strName = strName.substr( 1 );
		}
		else
		{
			QGetRenderContext() ->ClearDisplayRequests();
			QGetRenderContext() ->poptWriteCurrent()->GetIntegerOptionWrite( "System", "DisplayMode" ) [ 0 ] = eValue ;
		}
		// Add a display driver to the list of requested drivers.
		QGetRenderContext() ->AddDisplayRequest( strName.c_str(), strType.c_str(), dataName.c_str(), eValue, dataOffset, dataSize, mapOfArguments );
	}
}

//----------------------------------------------------------------------
// Specify a hidden surface calculation mode.
//
RtVoid RiCxxCore::Hider(RtConstToken name, const ParamList& pList)
{
	if ( !strcmp( name, "hidden" ) || !strcmp( name, "painter" ) )
	{
		QGetRenderContext() ->poptWriteCurrent()->GetStringOptionWrite( "System", "Hider" ) [ 0 ] = name ;
	}

	// Check options.
	int depthFilterIdx = pList.find(Ri::TypeSpec(Ri::TypeSpec::String),
								    "depthfilter");
	if(depthFilterIdx >= 0)
	{
		QGetRenderContext()->poptWriteCurrent()->
			GetStringOptionWrite("Hider", "depthfilter")[0] =
				pList[depthFilterIdx].stringData()[0];
	}
	int jitterIdx = pList.find(Ri::TypeSpec(Ri::TypeSpec::Integer), "jitter");
	if(jitterIdx >= 0)
	{
		QGetRenderContext()->poptWriteCurrent()->
			GetIntegerOptionWrite("Hider", "jitter")[0] =
				pList[jitterIdx].intData()[0];
	}
}


//----------------------------------------------------------------------
// Specify the depth and conversion arrays for color manipulation.
//
RtVoid RiCxxCore::ColorSamples(const FloatArray& nRGB, const FloatArray& RGBn)
{
	Aqsis::log() << warning << "RiColorSamples not supported" << std::endl;
}


//----------------------------------------------------------------------
// Set the scale used for all subsequent level of detail calculations.
//
RtVoid RiCxxCore::RelativeDetail(RtFloat relativedetail)
{
	if ( relativedetail < 0.0f )
	{
		Aqsis::log() << error << "RiRelativeDetail < 0.0" << std::endl;
	}
	else
	{
		QGetRenderContext() ->poptWriteCurrent()->GetFloatOptionWrite( "System", "RelativeDetail" ) [ 0 ] = relativedetail;
	}
}


//----------------------------------------------------------------------
// Specify system specific option.
//
RtVoid RiCxxCore::Option(RtConstToken name, const ParamList& pList)
{
	for(size_t i = 0; i < pList.size(); ++i)
	{
		const void* value = pList[i].data();

		TqInt Count = pList[i].spec().arraySize;
		const char* undecoratedName = pList[i].name();
		switch ( pList[i].spec().type )
		{
			case Ri::TypeSpec::Float:
			{
				const RtFloat* pf = reinterpret_cast<const RtFloat*>( value );
				TqFloat* pOpt = QGetRenderContext()->poptWriteCurrent()->GetFloatOptionWrite(name, undecoratedName, Count);
				RtInt j;
				for ( j = 0; j < Count; ++j )
					pOpt[ j ] = pf[ j ];
			}
			break;

			case Ri::TypeSpec::Integer:
			{
				const RtInt* pi = reinterpret_cast<const RtInt*>( value );
				TqInt* pOpt = QGetRenderContext()->poptWriteCurrent()->GetIntegerOptionWrite(name, undecoratedName, Count);
				RtInt j;
				for ( j = 0; j < Count; ++j )
					pOpt[ j ] = pi[ j ];
			}
			break;

			case Ri::TypeSpec::String:
			{
				const char* const* ps = reinterpret_cast<const char* const*>( value );
				CqString* pOpt = QGetRenderContext()->poptWriteCurrent()
					->GetStringOptionWrite(name, undecoratedName, Count);
				RtInt j;
				for ( j = 0; j < Count; ++j )
				{
					if ( strcmp( name, "searchpath" ) == 0 )
					{
						// Get the default string for use in escape replacement
						const CqString* pDefSearch
							= QGetRenderContext()->poptWriteCurrent()
							->GetStringOption("defaultsearchpath", undecoratedName);
						std::string defaultSearch;
						if(pDefSearch)
							defaultSearch = *pDefSearch;
						Aqsis::log() << debug << "Old " << undecoratedName << " searchpath = " << pOpt[j] << std::endl;
						pOpt[j] = expandSearchPath(ps[j], pOpt[j], defaultSearch);
						Aqsis::log() << debug << "New " << undecoratedName << " searchpath = " << pOpt[j] << std::endl;
					}
					else if ( strcmp( name, "defaultsearchpath" ) == 0 )
					{
						Aqsis::log() << debug << "Old " << undecoratedName << " defaultsearchpath = " << pOpt[j] << std::endl;
						pOpt[j] = expandSearchPath(ps[j], pOpt[j], std::string());
						Aqsis::log() << debug << "New " << undecoratedName << " defaultsearchpath = " << pOpt[j] << std::endl;
					}
					else
						pOpt[j] = ps[j];
				}
			}
			break;

			case Ri::TypeSpec::Color:
			{
				const RtFloat* pc = reinterpret_cast<const RtFloat*>( value );
				CqColor* pOpt = QGetRenderContext()->poptWriteCurrent()->GetColorOptionWrite(name, undecoratedName, Count);
				RtInt j;
				for ( j = 0; j < Count; ++j )
					pOpt[ j ] = CqColor(pc[ (j*3) ], pc[ (j*3)+1 ], pc[ (j*3)+2 ]);
			}
			break;

			case Ri::TypeSpec::Point:
			{
				const RtFloat* pc = reinterpret_cast<const RtFloat*>( value );
				CqVector3D* pOpt = QGetRenderContext()->poptWriteCurrent()->GetPointOptionWrite(name, undecoratedName, Count);
				RtInt j;
				for ( j = 0; j < Count; ++j )
					pOpt[ j ] = CqVector3D(pc[ (j*3) ], pc[ (j*3)+1 ], pc[ (j*3)+2 ]);
			}
			break;

			case Ri::TypeSpec::Normal:
			{
				const RtFloat* pc = reinterpret_cast<const RtFloat*>( value );
				CqVector3D* pOpt = QGetRenderContext()->poptWriteCurrent()->GetPointOptionWrite(name, undecoratedName, Count);
				RtInt j;
				for ( j = 0; j < Count; ++j )
					pOpt[ j ] = CqVector3D(pc[ (j*3) ], pc[ (j*3)+1 ], pc[ (j*3)+2 ]);
			}
			break;

			case Ri::TypeSpec::Vector:
			{
				const RtFloat* pc = reinterpret_cast<const RtFloat*>( value );
				CqVector3D* pOpt = QGetRenderContext()->poptWriteCurrent()->GetPointOptionWrite(name, undecoratedName, Count);
				RtInt j;
				for ( j = 0; j < Count; ++j )
					pOpt[ j ] = CqVector3D(pc[ (j*3) ], pc[ (j*3)+1 ], pc[ (j*3)+2 ]);
			}
			break;

			case Ri::TypeSpec::HPoint:
			{
/*				RtFloat* pc = reinterpret_cast<RtFloat*>( value );
				CqVector4D* pOpt = QGetRenderContext()->poptWriteCurrent()->GetHPointOptionWrite(name, undecoratedName, Count);
				RtInt j;
				for ( j = 0; j < Count; ++j )
					pOpt[ j ] = CqVector4D(pc[ (j*4) ], pc[ (j*4)+1 ], pc[ (j*4)+2 ], pc[ (j*4)+3]); */
			}
			break;

			case Ri::TypeSpec::Matrix:
			{
/*				RtFloat* pc = reinterpret_cast<RtFloat*>( value );
				CqMatrix* pOpt = QGetRenderContext()->poptWriteCurrent()->GetMatrixOptionWrite(name, undecoratedName, Count);
				RtInt j;
				for ( j = 0; j < Count; ++j )
					pOpt[ j ] = CqMatrix(pm[ j    ], pm[ j+1  ], pm[ j+2  ], pm[ j+3  ],
							        pm[ j+4  ], pm[ j+5  ], pm[ j+6  ], pm[ j+7  ],
							        pm[ j+8  ], pm[ j+9  ], pm[ j+10 ], pm[ j+11 ],
							        pm[ j+12 ], pm[ j+13 ], pm[ j+14 ], pm[ j+15 ]); */
			}
			break;

			default:
			break;
		}
	}
}


//----------------------------------------------------------------------
// Begin a ne attribute definition, pushes the current attributes.
//
RtVoid RiCxxCore::AttributeBegin()
{
	QGetRenderContext() ->BeginAttributeModeBlock();
}


//----------------------------------------------------------------------
// End the current attribute defintion, pops the previous attributes.
//
RtVoid RiCxxCore::AttributeEnd()
{
	QGetRenderContext() ->EndAttributeModeBlock();
}


//----------------------------------------------------------------------
//	Set the current color for use by the geometric primitives.
//
RtVoid RiCxxCore::Color(RtConstColor Cq)
{
	QGetRenderContext() ->pattrWriteCurrent() ->GetColorAttributeWrite( "System", "Color" ) [ 0 ] = CqColor( Cq );
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Set the current opacity, for use by the geometric primitives.
//
RtVoid RiCxxCore::Opacity(RtConstColor Os)
{
	QGetRenderContext() ->pattrWriteCurrent() ->GetColorAttributeWrite( "System", "Opacity" ) [ 0 ] = CqColor( Os );
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Set the current texture coordinates used by the parametric geometric primitives.
//
RtVoid RiCxxCore::TextureCoordinates(RtFloat s1, RtFloat t1,
									 RtFloat s2, RtFloat t2,
									 RtFloat s3, RtFloat t3,
									 RtFloat s4, RtFloat t4)
{
	TqFloat * pTC = QGetRenderContext() ->pattrWriteCurrent() ->GetFloatAttributeWrite( "System", "TextureCoordinates" );

	assert( NULL != pTC );

	pTC[ 0 ] = s1;
	pTC[ 1 ] = t1;
	pTC[ 2 ] = s2;
	pTC[ 3 ] = t2;
	pTC[ 4 ] = s3;
	pTC[ 5 ] = t3;
	pTC[ 6 ] = s4;
	pTC[ 7 ] = t4;
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Create a new light source at the current transformation.
//
RtVoid RiCxxCore::LightSource(RtConstToken shadername, RtConstToken name, const ParamList& pList)
{
	// Find the lightsource shader.
	boost::shared_ptr<IqShader> pShader = QGetRenderContext()->CreateShader( shadername, Type_Lightsource );
	if(!pShader)
		return;

	pShader->SetTransform( QGetRenderContext() ->ptransCurrent() );
	CqLightsourcePtr pNew( new CqLightsource( pShader, RI_TRUE ) );
	QGetRenderContext()->registerLight(name, pNew);

	// Execute the intiialisation code here, as we now have our shader context complete.
	pShader->PrepareDefArgs();

	if ( pNew )
	{
		setShaderArguments(pShader, pList);
		QGetRenderContext() ->pattrWriteCurrent() ->AddLightsource( pNew );
		// If this light is being defined outside the WorldBegin, then we can
		// go ahead and initialise the parameters, as they are invariant under changes to the camera space.
		if(!QGetRenderContext()->IsWorldBegin())
			pShader->InitialiseParameters();

		const TqInt* pMultipass = QGetRenderContext()->poptCurrent()->GetIntegerOption("Render", "multipass");
		if(pMultipass && !pMultipass[0])
			pShader->PrepareShaderForUse();

		// Add it as a Context light as well in case we are in a context that manages it's own lights.
		QGetRenderContext() ->pconCurrent() ->AddContextLightSource( pNew );
	}
}


//----------------------------------------------------------------------
// Create a new area light source at the current transformation, all
// geometric primitives until the next RiAttributeEnd, become part of this
// area light source.
//
RtVoid RiCxxCore::AreaLightSource(RtConstToken shadername, RtConstToken name, const ParamList& pList)
{
	Aqsis::log() << warning << "RiAreaLightSource not supported, will produce a point light" << std::endl;

	LightSource(shadername, name, pList);
}


//----------------------------------------------------------------------
// Set the current status of the specified light source.
//
RtVoid RiCxxCore::Illuminate(RtConstToken name, RtBoolean onoff)
{
	CqLightsourcePtr pL = QGetRenderContext()->findLight(name);

	if ( onoff )
		QGetRenderContext() ->pattrWriteCurrent() ->AddLightsource( pL );
	else
		QGetRenderContext() ->pattrWriteCurrent() ->RemoveLightsource( pL );
}


//----------------------------------------------------------------------
// Set the current surface shader, used by geometric primitives.
//
RtVoid RiCxxCore::Surface(RtConstToken name, const ParamList& pList)
{
	// Find the shader.
	boost::shared_ptr<IqShader> pshadSurface = QGetRenderContext()->CreateShader( name, Type_Surface );

	if ( pshadSurface )
	{
		pshadSurface->SetTransform( QGetRenderContext() ->ptransCurrent() );
		// Execute the intiialisation code here, as we now have our shader context complete.
		pshadSurface->PrepareDefArgs();
		setShaderArguments(pshadSurface, pList);

		const TqInt* pMultipass = QGetRenderContext()->poptCurrent()->GetIntegerOption("Render", "multipass");
		if(pMultipass && !pMultipass[0])
			pshadSurface->PrepareShaderForUse();

		QGetRenderContext() ->pattrWriteCurrent() ->SetpshadSurface( pshadSurface, QGetRenderContext() ->Time() );
	}
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Set the current atrmospheric shader.
//
RtVoid RiCxxCore::Atmosphere(RtConstToken name, const ParamList& pList)
{
	// Find the shader.
	boost::shared_ptr<IqShader> pshadAtmosphere = QGetRenderContext()->CreateShader( name, Type_Volume );

	if ( pshadAtmosphere )
	{
		pshadAtmosphere->SetTransform( QGetRenderContext() ->ptransCurrent() );
		// Execute the intiialisation code here, as we now have our shader context complete.
		pshadAtmosphere->PrepareDefArgs();
		setShaderArguments(pshadAtmosphere, pList);
		const TqInt* pMultipass = QGetRenderContext()->poptCurrent()->GetIntegerOption("Render", "multipass");
		if(pMultipass && !pMultipass[0])
			pshadAtmosphere->PrepareShaderForUse();
	}

	QGetRenderContext() ->pattrWriteCurrent() ->SetpshadAtmosphere( pshadAtmosphere, QGetRenderContext() ->Time() );
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Set the current interior volumetric shader.
//
RtVoid RiCxxCore::Interior(RtConstToken name, const ParamList& pList)
{
	Aqsis::log() << warning << "RiInterior not supported" << std::endl;
}


//----------------------------------------------------------------------
// Set the current exterior volumetric shader.
//
RtVoid RiCxxCore::Exterior(RtConstToken name, const ParamList& pList)
{
	Aqsis::log() << warning << "ExInterior not supported" << std::endl;
}


//----------------------------------------------------------------------
// Specify the size of the shading area in pixels.
//
RtVoid RiCxxCore::ShadingRate(RtFloat size)
{
	QGetRenderContext() ->pattrWriteCurrent() ->GetFloatAttributeWrite( "System", "ShadingRate" ) [ 0 ] = size;
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Specify the method of shading interpolation.
//
RtVoid RiCxxCore::ShadingInterpolation(RtConstToken type)
{
	if ( strcmp( type, RI_CONSTANT ) == 0 )
		QGetRenderContext() ->pattrWriteCurrent() ->GetIntegerAttributeWrite( "System", "ShadingInterpolation" ) [ 0 ] = ShadingInterp_Constant;
	else if ( strcmp( type, RI_SMOOTH ) == 0 )
		QGetRenderContext() ->pattrWriteCurrent() ->GetIntegerAttributeWrite( "System", "ShadingInterpolation" ) [ 0 ] = ShadingInterp_Smooth;
	else
		Aqsis::log() << error << "RiShadingInterpolation unrecognised value \"" << type << "\"" << std::endl;

	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Set the matte state of subsequent geometric primitives.
//
RtVoid RiCxxCore::Matte(RtBoolean onoff)
{
	QGetRenderContext() ->pattrWriteCurrent() ->GetIntegerAttributeWrite( "System", "Matte" ) [ 0 ] = onoff;
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Set the bounding cube of the current primitives.
//
RtVoid RiCxxCore::Bound(RtConstBound bound)
{
	// TODO: Need to add a "Bound" attribute here, and fill it in.
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Set the current bounding cube for use by level of detail calculation.
//
RtVoid RiCxxCore::Detail(RtConstBound bound)
{
	CqBound Bound( bound );

	TqFloat* boundAttr = QGetRenderContext() ->pattrWriteCurrent() ->GetFloatAttributeWrite( "System", "LODBound" );
	boundAttr[0] = bound[0];
	boundAttr[1] = bound[1];
	boundAttr[2] = bound[2];
	boundAttr[3] = bound[3];
	boundAttr[4] = bound[4];
	boundAttr[5] = bound[5];
}


//----------------------------------------------------------------------
// Set the visible range of any subsequent geometric primitives.
//
RtVoid RiCxxCore::DetailRange(RtFloat offlow, RtFloat onlow, RtFloat onhigh, RtFloat offhigh)
{
	if ( offlow > onlow || onhigh > offhigh )
	{
		Aqsis::log() << error << "RiDetailRange invalid range" << std::endl;
		return ;
	}

	TqFloat* rangeAttr = QGetRenderContext() ->pattrWriteCurrent() ->GetFloatAttributeWrite( "System", "LODRanges" );
	rangeAttr[0] = offlow;
	rangeAttr[1] = onlow;
	rangeAttr[2] = onhigh;
	rangeAttr[3] = offhigh;
}


//----------------------------------------------------------------------
// Specify any parameters used by approximation functions during rendering.
//
RtVoid RiCxxCore::GeometricApproximation(RtConstToken type, RtFloat value)
{
	std::string typeStr = type;
	if(typeStr == RI_FLATNESS)
	{
		TqFloat* flatnessAttr = QGetRenderContext()->pattrWriteCurrent()->
			GetFloatAttributeWrite("System", "GeometricFlatness");
		flatnessAttr[0] = value;
		Aqsis::log() << warning
			<< "RiGeometricApproximation flatness test not yet implemented\n";
	}
	else if(typeStr == "focusfactor")
	{
		TqFloat* focusFactorAttr = QGetRenderContext()->pattrWriteCurrent()->
			GetFloatAttributeWrite("System", "GeometricFocusFactor");
		focusFactorAttr[0] = value;
	}
	else if(typeStr == "motionfactor")
	{
		TqFloat* motionFactorAttr = QGetRenderContext()->pattrWriteCurrent()->
			GetFloatAttributeWrite("System", "GeometricMotionFactor");
		motionFactorAttr[0] = value;
	}
	else
	{
		Aqsis::log() << warning << "RiGeometricApproximation type not known\n";
	}
}


//----------------------------------------------------------------------
// Set the handedness of any subsequent geometric primitives.
//
RtVoid RiCxxCore::Orientation(RtConstToken orientation)
{
	if ( orientation != 0 )
	{
		if ( strstr( orientation, RI_RH ) != 0 )
			QGetRenderContext() ->pattrWriteCurrent() ->GetIntegerAttributeWrite( "System", "Orientation" ) [ 0 ] = ( QGetRenderContext() ->ptransCurrent()->GetHandedness(QGetRenderContext()->Time()) ) ? 0 : 1;
		if ( strstr( orientation, RI_LH ) != 0 )
			QGetRenderContext() ->pattrWriteCurrent() ->GetIntegerAttributeWrite( "System", "Orientation" ) [ 0 ] = ( QGetRenderContext() ->ptransCurrent()->GetHandedness(QGetRenderContext()->Time()) ) ? 1 : 0;
		if ( strstr( orientation, RI_INSIDE ) != 0 )
			QGetRenderContext() ->pattrWriteCurrent() ->GetIntegerAttributeWrite( "System", "Orientation" ) [ 0 ] = 1;
		if ( strstr( orientation, RI_OUTSIDE ) != 0 )
			QGetRenderContext() ->pattrWriteCurrent() ->GetIntegerAttributeWrite( "System", "Orientation" ) [ 0 ] = 0;
	}
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Reverse the handedness of any subsequent geometric primitives.
//
RtVoid RiCxxCore::ReverseOrientation()
{
	QGetRenderContext() ->pattrWriteCurrent() ->FlipeOrientation( QGetRenderContext() ->Time() );
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Set the number of visibles sides for any subsequent geometric primitives.
//
RtVoid RiCxxCore::Sides(RtInt nsides)
{
	QGetRenderContext() ->pattrWriteCurrent() ->GetIntegerAttributeWrite( "System", "Sides" ) [ 0 ] = nsides;
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Set the current transformation to the identity matrix.
//
RtVoid RiCxxCore::Identity()
{
	QGetRenderContext() ->ptransSetTime( CqMatrix() );
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Set the current transformation to the specified matrix.
//
RtVoid RiCxxCore::Transform(RtConstMatrix transform)
{
	CqMatrix matTrans( transform );
	//    if ( matTrans.Determinant() < 0 && ( QGetRenderContext()->pconCurrent()->Type() != Motion || QGetRenderContext()->pconCurrent()->TimeIndex() == 0 ) )
	//        QGetRenderContext() ->ptransWriteCurrent() ->FlipHandedness( QGetRenderContext() ->Time() );

	if( QGetRenderContext()->IsWorldBegin() )
	{
		// If we're in the WorldBegin/End block, we need to take the 'camera' transform as the starting point.
		// This is because we have to transform primitives to 'world' coordinates.
		// So we read the 'default object transform' that is stored at 'RiWorldBegin', set the transform at the current time to that, then concat the specified transform.
		CqMatrix matDefTrans( QGetRenderContext()->GetDefObjTransform()->matObjectToWorld(QGetRenderContext()->Time()) );
		QGetRenderContext() ->ptransSetTime( matDefTrans );
		QGetRenderContext() ->ptransConcatCurrentTime( matTrans );
	}
	else
		QGetRenderContext() ->ptransSetTime( matTrans );
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Concatenate the specified matrix into the current transformation matrix.
//
RtVoid RiCxxCore::ConcatTransform(RtConstMatrix transform)
{
	// Check if this transformation results in a change in orientation.
	CqMatrix matTrans( transform );
	//    if ( matTrans.Determinant() < 0 && ( QGetRenderContext()->pconCurrent()->Type() != Motion || QGetRenderContext()->pconCurrent()->TimeIndex() == 0 ) )
	//        QGetRenderContext() ->pattrWriteCurrent() ->FlipeCoordsysOrientation( QGetRenderContext() ->Time() );

	QGetRenderContext() ->ptransConcatCurrentTime( CqMatrix( transform ) );
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Concatenate a perspective transformation into the current transformation.
//
RtVoid RiCxxCore::Perspective(RtFloat fov)
{
	fov = tan( degToRad( fov / 2 ) );

	// This matches PRMan 3.9 in testing, but not BMRT 2.6's rgl and rendrib.
	CqMatrix	matP( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, fov, fov, 0, 0, -fov, 0 );

	// Check if this transformation results in a change in orientation.
	//    if ( matP.Determinant() < 0 && ( QGetRenderContext()->pconCurrent()->Type() != Motion || QGetRenderContext()->pconCurrent()->TimeIndex() == 0 ) )
	//        QGetRenderContext() ->pattrWriteCurrent() ->FlipeCoordsysOrientation( QGetRenderContext() ->Time() );

	QGetRenderContext() ->ptransConcatCurrentTime( matP );
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Concatenate a translation into the current transformation.
//
RtVoid RiCxxCore::Translate(RtFloat dx, RtFloat dy, RtFloat dz)
{
	CqMatrix	matTrans( CqVector3D( dx, dy, dz ) );
	// Check if this transformation results in a change in orientation.
	//    if ( matTrans.Determinant() < 0 && ( QGetRenderContext()->pconCurrent()->Type() != Motion || QGetRenderContext()->pconCurrent()->TimeIndex() == 0 ) )
	//        QGetRenderContext() ->pattrWriteCurrent() ->FlipeCoordsysOrientation( QGetRenderContext() ->Time() );

	QGetRenderContext() ->ptransConcatCurrentTime( matTrans );
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Concatenate a rotation into the current transformation.
//
RtVoid RiCxxCore::Rotate(RtFloat angle, RtFloat dx, RtFloat dy, RtFloat dz)
{
	CqMatrix	matRot( degToRad( angle ), CqVector3D( dx, dy, dz ) );
	// Check if this transformation results in a change in orientation.
	//    if ( matRot.Determinant() < 0 && ( QGetRenderContext()->pconCurrent()->Type() != Motion || QGetRenderContext()->pconCurrent()->TimeIndex() == 0 ) )
	//        QGetRenderContext() ->pattrWriteCurrent() ->FlipeCoordsysOrientation( QGetRenderContext() ->Time() );

	QGetRenderContext() ->ptransConcatCurrentTime( matRot );
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Concatenate a scale into the current transformation.
//
RtVoid RiCxxCore::Scale(RtFloat sx, RtFloat sy, RtFloat sz)
{
	CqMatrix	matScale( sx, sy, sz );
	// Check if this transformation results in a change in orientation.
	//    if ( matScale.Determinant() < 0 && ( QGetRenderContext()->pconCurrent()->Type() != Motion || QGetRenderContext()->pconCurrent()->TimeIndex() == 0 ) )
	//        QGetRenderContext() ->pattrWriteCurrent() ->FlipeCoordsysOrientation( QGetRenderContext() ->Time() );

	QGetRenderContext() ->ptransConcatCurrentTime( matScale );
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Concatenate a skew into the current transformation.
//
RtVoid RiCxxCore::Skew(RtFloat angle, RtFloat dx1, RtFloat dy1, RtFloat dz1, RtFloat dx2, RtFloat dy2, RtFloat dz2)
{
	CqMatrix	matSkew( degToRad( angle ), dx1, dy1, dz1, dx2, dy2, dz2 );

	// This transformation can not change orientation.

	QGetRenderContext() ->ptransConcatCurrentTime( matSkew );
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Specify the current displacement shade used by geometric primitives.
//
RtVoid RiCxxCore::Displacement(RtConstToken name, const ParamList& pList)
{
	// Find the shader.
	boost::shared_ptr<IqShader> pshadDisplacement = QGetRenderContext() ->CreateShader( name, Type_Displacement );

	if ( pshadDisplacement )
	{
		pshadDisplacement->SetTransform( QGetRenderContext() ->ptransCurrent() );
		// Execute the intiialisation code here, as we now have our shader context complete.
		pshadDisplacement->PrepareDefArgs();
		setShaderArguments(pshadDisplacement, pList);
		const TqInt* pMultipass = QGetRenderContext()->poptCurrent()->GetIntegerOption("Render", "multipass");
		if(pMultipass && !pMultipass[0])
			pshadDisplacement->PrepareShaderForUse();
	}

	QGetRenderContext() ->pattrWriteCurrent() ->SetpshadDisplacement( pshadDisplacement, QGetRenderContext() ->Time() );
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Save the current coordinate system as the specified name.
//
RtVoid RiCxxCore::CoordinateSystem(RtConstToken space)
{
	// Insert the named coordinate system into the list help on the renderer.
	QGetRenderContext() ->SetCoordSystem( space, QGetRenderContext() ->matCurrent( QGetRenderContext() ->Time() ) );
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// ---Additional to spec. v3.1---
// Replace the current transform with the named space.

RtVoid RiCxxCore::CoordSysTransform(RtConstToken space)
{
	// Insert the named coordinate system into the list help on the renderer.
	CqMatrix matSpaceToWorld;
	QGetRenderContext() ->matSpaceToSpace( space, "world", NULL, NULL, QGetRenderContext()->Time(), matSpaceToWorld ); 

	if( QGetRenderContext()->IsWorldBegin() )
	{
		// If we're in the WorldBegin/End block, we need to take the 'camera' transform as the starting point.
		// This is because we have to transform primitives to 'world' coordinates.
		// So we read the 'default object transform' that is stored at 'RiWorldBegin', set the transform at the current time to that, then concat the specified transform.
		CqMatrix matDefTrans( QGetRenderContext()->GetDefObjTransform()->matObjectToWorld(QGetRenderContext()->Time()) );
		QGetRenderContext() ->ptransSetCurrentTime( matDefTrans );
		QGetRenderContext() ->ptransConcatCurrentTime( matSpaceToWorld );
	}
	else
		QGetRenderContext() ->ptransSetCurrentTime( matSpaceToWorld );

	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Push the current transformation state.
//
RtVoid RiCxxCore::TransformBegin()
{
	QGetRenderContext() ->BeginTransformModeBlock();
}


//----------------------------------------------------------------------
// Pop the previous transformation state.
//
RtVoid RiCxxCore::TransformEnd()
{
	QGetRenderContext() ->EndTransformModeBlock();
}


//----------------------------------------------------------------------
// Set a system specific attribute.
//
RtVoid RiCxxCore::Attribute(RtConstToken name, const ParamList& pList)
{
	// Find the parameter on the current options.
	CqNamedParameterList * pAttr = QGetRenderContext() ->pattrWriteCurrent() ->pAttributeWrite( name ).get();

	for(size_t i = 0; i < pList.size(); ++i)
	{
		CqParameter* pParam = pAttr->pParameter(tokenString(pList[i]).c_str());
		if ( pParam == 0 )
		{
			// Search for the parameter in the declarations.
			// Note attributes can only be uniform.
			if ( pList[i].spec().iclass == Ri::TypeSpec::Uniform )
			{
				pParam = CqParameter::Create(CqPrimvarToken(pList[i].spec(),
															pList[i].name()));
				pAttr->AddParameter( pParam );
			}
			else
			{
				Aqsis::log() << warning << "RiAttribute: Attributes can only be uniform [" << pList[i].name() << "]\n";
				continue;
			}
		}
		bool bArray = pParam->Count() > 1;

		const void* value = pList[i].data();
		switch ( pParam->Type() )
		{
			case type_float:
				{
					const RtFloat* pf = reinterpret_cast<const RtFloat*>( value );
					if ( bArray )
					{
						RtInt j;
						for ( j = 0; j < pParam->Count(); ++j )
							static_cast<CqParameterTypedUniformArray<RtFloat, type_float, RtFloat>*>( pParam ) ->pValue() [ j ] = pf[ j ];
					}
					else
						static_cast<CqParameterTypedUniform<RtFloat, type_float, RtFloat>*>( pParam ) ->pValue() [ 0 ] = pf[ 0 ];
				}
				break;

			case type_integer:
				{
					const RtInt* pi = reinterpret_cast<const RtInt*>( value );
					if ( bArray )
					{
						RtInt j;
						for ( j = 0; j < pParam->Count(); ++j )
							static_cast<CqParameterTypedUniformArray<RtInt, type_integer, RtFloat>*>( pParam ) ->pValue() [ j ] = pi[ j ];
					}
					else
						static_cast<CqParameterTypedUniform<RtInt, type_integer, RtFloat>*>( pParam ) ->pValue() [ 0 ] = pi[ 0 ];
				}
				break;

			case type_string:
				{
					const char* const* ps = reinterpret_cast<const char* const*>( value );
					if ( bArray )
					{
						RtInt j;
						for ( j = 0; j < pParam->Count(); ++j )
						{
							CqString str( ps[ j ] );
							static_cast<CqParameterTypedUniform<CqString, type_string, CqString>*>( pParam ) ->pValue() [ j ] = str;
						}
					}
					else
					{
						CqString str( ps[ 0 ] );
						static_cast<CqParameterTypedUniform<CqString, type_string, CqString>*>( pParam ) ->pValue() [ 0 ] = str;
					}
				}
				break;
				// TODO: Rest of parameter types.
			default:
				Aqsis::log() << warning
					<< "RiAttribute \"" << name
					<< "\": unimplemented attribute type ["
					<< tokenString(pList[i]) << "]\n";
		}
	}
}


//----------------------------------------------------------------------
// Specify a coplanar, convex polygon.
//
RtVoid RiCxxCore::Polygon(const ParamList& pList)
{
	RtInt nvertices = countP(pList);
	// Create a new polygon surface primitive.
	boost::shared_ptr<CqSurfacePolygon> pSurface( new CqSurfacePolygon( nvertices ) );

	// Process any specified primitive variables.
	if ( ProcessPrimitiveVariables( pSurface.get(), pList ) )
	{
		if ( !pSurface->CheckDegenerate() )
		{
			TqFloat time = QGetRenderContext()->Time();
			// Transform the points into camera space for processing,
			CqMatrix matOtoW, matNOtoW, matVOtoW;
			QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
			QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
			QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
			pSurface->Transform( matOtoW, matNOtoW, matVOtoW);
			CreateGPrim( pSurface );
		}
		else
		{
			Aqsis::log() << error << "Found degenerate polygon" << std::endl;
		}
	}
}


//----------------------------------------------------------------------
// Specify a nonconvex coplanar polygon.
//
RtVoid RiCxxCore::GeneralPolygon(const IntArray& nverts, const ParamList& pList)
{
	RtInt nloops = nverts.size();
	TqInt iloop;

	// Calcualte how many points there are.
	TqInt cVerts = 0;
	for ( iloop = 0; iloop < nloops; ++iloop )
	{
		cVerts += nverts[ iloop ];
		// Check for degenerate loops.
		if( nverts[ iloop ] < 3 )
		{
			CqString objname( "unnamed" );
			const CqString* pattrName = QGetRenderContext()->pattrCurrent()->GetStringAttribute( "identifier", "name" );
			if ( pattrName != 0 )
				objname = pattrName[ 0 ];
			Aqsis::log() << warning << "Degenerate loop in GeneralPolygon object \"" << objname.c_str() << "\"" << std::endl;
		}
	}

	// Create a storage class for all the points.
	boost::shared_ptr<CqPolygonPoints> pPointsClass( new CqPolygonPoints( cVerts, 1, cVerts ) );
	// Process any specified primitive variables
	if ( ProcessPrimitiveVariables( pPointsClass.get(), pList ) )
	{
		pPointsClass->SetDefaultPrimitiveVariables( RI_FALSE );

		// Work out which plane to project to.
		TqFloat	MinX, MaxX;
		TqFloat	MinY, MaxY;
		TqFloat	MinZ, MaxZ;
		CqVector3D vecTemp = vectorCast<CqVector3D>(pPointsClass->P()->pValue(0)[0]);
		MinX = MaxX = vecTemp.x();
		MinY = MaxY = vecTemp.y();
		MinZ = MaxZ = vecTemp.z();

		// We need to take into account Orientation here.
		bool O = QGetRenderContext()->pattrCurrent() ->GetIntegerAttribute( "System", "Orientation" ) [ 0 ] != 0;

		TqUint iVert;
		for ( iVert = 1; iVert < pPointsClass->P() ->Size(); ++iVert )
		{
			vecTemp = vectorCast<CqVector3D>(pPointsClass->P()->pValue(iVert)[0]);
			MinX = ( MinX < vecTemp.x() ) ? MinX : vecTemp.x();
			MinY = ( MinY < vecTemp.y() ) ? MinY : vecTemp.y();
			MinZ = ( MinZ < vecTemp.z() ) ? MinZ : vecTemp.z();
			MaxX = ( MaxX > vecTemp.x() ) ? MaxX : vecTemp.x();
			MaxY = ( MaxY > vecTemp.y() ) ? MaxY : vecTemp.y();
			MaxZ = ( MaxZ > vecTemp.z() ) ? MaxZ : vecTemp.z();
		}
		TqFloat	DiffX = MaxX - MinX;
		TqFloat	DiffY = MaxY - MinY;
		TqFloat	DiffZ = MaxZ - MinZ;

		TqInt Axis;
		if ( DiffX < DiffY && DiffX < DiffZ )
			Axis = CqPolygonGeneral2D::Axis_YZ;
		else if ( DiffY < DiffX && DiffY < DiffZ )
			Axis = CqPolygonGeneral2D::Axis_XZ;
		else
			Axis = CqPolygonGeneral2D::Axis_XY;

		// Create a general 2D polygon using the points in each loop.
		CqPolygonGeneral2D poly;
		TqUint ipoint = 0;
		for ( iloop = 0; iloop < nloops; ++iloop )
		{
			CqPolygonGeneral2D polya;
			polya.SetAxis( Axis );
			polya.SetpVertices( pPointsClass );
			TqInt ivert;
			for ( ivert = 0; ivert < nverts[ iloop ]; ++ivert )
			{
				assert( ipoint < pPointsClass->P() ->Size() );
				polya.aiVertices().push_back( ipoint++ );
			}
			if ( iloop == 0 )
			{
				/// \note: We need to check here if the orientation of the projected poly matches the
				/// expected one, of not, we must swap the direction so that the triangulation routines can
				/// correctly determine the inside/outside nature of points. However, if doing so breaks the
				/// orientation as expected by the rest of the renderer, we need to flip the orientation
				/// attribute as well so that normals are correctly calculated.
				if( O )
				{
					if ( polya.CalcOrientation() != CqPolygonGeneral2D::Orientation_AntiClockwise )
					{
						QGetRenderContext() ->pattrWriteCurrent()->GetIntegerAttributeWrite( "System", "Orientation" ) [ 0 ] = 0;
						polya.SwapDirection();
					}
				}
				else
				{
					if ( polya.CalcOrientation() != CqPolygonGeneral2D::Orientation_Clockwise )
					{
						QGetRenderContext() ->pattrWriteCurrent()->GetIntegerAttributeWrite( "System", "Orientation" ) [ 0 ] = 1;
						polya.SwapDirection();
					}
				}
				poly = polya;
			}
			else
			{
				if( O )
				{
					if ( polya.CalcOrientation() != CqPolygonGeneral2D::Orientation_Clockwise )
						polya.SwapDirection();
				}
				else
				{
					if ( polya.CalcOrientation() != CqPolygonGeneral2D::Orientation_AntiClockwise )
						polya.SwapDirection();
				}
				poly.Combine( polya );
			}
		}
		// Now triangulate the general polygon

		std::vector<TqInt>	aiTriangles;
		poly.CalcOrientation();
		poly.Triangulate( aiTriangles );

		TqUint ctris = aiTriangles.size() / 3;
		// Build an array of point counts (always 3 each).
		std::vector<RtInt> _nverts;
		_nverts.resize( ctris, 3 );

		PointsPolygons(Ri::IntArray(&_nverts[0], _nverts.size()),
					   Ri::IntArray(&aiTriangles[0], aiTriangles.size()), pList);
	}
}

//----------------------------------------------------------------------
RtVoid RiCxxCore::Blobby(RtInt nleaf, const IntArray& code, const FloatArray& flt, const StringArray& strings, const ParamList& pList)
{
	TqInt i;

	// Initialize the blobby structure
	CqBlobby blobby(nleaf, code.size(), const_cast<TqInt*>(code.begin()), flt.size(),
					const_cast<TqFloat*>(flt.begin()), strings.size(), const_cast<char**>(strings.begin()));

	// Get back the bounding box in world coordinates
	CqBound Bound;
	blobby.Bound(&Bound);

	// Transform the bounding box into camera coordinates
	CqMatrix matOtoC;
	QGetRenderContext() ->matSpaceToSpace( "object", "camera", NULL, QGetRenderContext() ->ptransCurrent().get(), QGetRenderContext()->Time(), matOtoC );
	Bound.Transform( matOtoC );

	// The bounding-box stops at camera's plane
	TqFloat camera_z = QGetRenderContext() ->poptCurrent() ->GetFloatOption( "System", "Clipping" ) [ 0 ];
	if(Bound.vecMax().z() < camera_z)
		// Blobby's behind the camera
		return;

	if(Bound.vecMin().z() < camera_z)
		// Cut the bounding-box with camera's plane
		Bound = CqBound(CqVector3D(Bound.vecMin().x(), Bound.vecMin().y(), camera_z), Bound.vecMax());

	// Transform the bounding box into raster coordinates
	CqMatrix matCamToRaster;
	QGetRenderContext() ->matSpaceToSpace( "camera", "raster", NULL, QGetRenderContext() ->ptransCurrent().get(), QGetRenderContext()->Time(), matCamToRaster );
	Bound.Transform( matCamToRaster );

	// Get bounding-box size in pixels
	TqInt  pixels_w = static_cast<TqInt> ( Bound.vecCross().x() );
	TqInt  pixels_h = static_cast<TqInt> ( Bound.vecCross().y() );

	// Adjust to shading rate
	// TODO: Blobbies should be CqSurfaces - in that case they could make of
	// the AdjustedShadingRate() function to adjust the shading for DoF and MB
	TqInt shading_rate = max(1, static_cast<TqInt> ( QGetRenderContext() ->pattrCurrent() ->GetFloatAttribute( "System", "ShadingRate" ) [ 0 ]));
	pixels_w /= shading_rate;
	pixels_h /= shading_rate;


	// Polygonize this blobby
	TqInt npoints;
	TqInt npolygons;
	TqInt* nvertices = 0;
	TqInt* vertices = 0;
	TqFloat* points = 0;
	TqInt pieces = blobby.polygonize(pixels_w, pixels_h, npoints, npolygons, nvertices, vertices, points);

	Aqsis::log() << info << "Polygonized : " << npoints << " points, " << npolygons << " triangles." << std::endl;

	TqFloat* colors = new TqFloat[3 * npoints];

	std::vector<TqFloat> splits;
	splits.resize(nleaf);

	bool Cs = false;
	for (size_t c = 0; c < pList.size(); c++)
	{
		if (!strcmp(pList[c].name(), RI_CS))
		{
			CqVector3D cg;

			for( int i = 0; i < npoints; i++ )
			{
				TqFloat sum;
				TqFloat ocolors[3] = {0.0f,0.0f,0.0f};

				TqInt m = i * 3;
				cg[0] = points[m];
				cg[1] = points[m + 1];
				cg[2] = points[m + 2];

				sum = blobby.implicit_value(cg, nleaf, splits);

				if (sum != 0.0)
				{
					colors[m] = colors[m+1] = colors[m+2] = 0.0f;
					const TqFloat* CsValue = pList[c].floatData().begin();
					for (TqInt j=0; j < nleaf; j++)
					{
						TqInt l = j * 3;
						colors[m] += splits[j] * CsValue[l];
						colors[m+1] += splits[j] * CsValue[l+1];
						colors[m+2] += splits[j] * CsValue[l+2];
					}
					colors[m]/=sum;
					colors[m+1]/=sum;
					colors[m+2]/=sum;
					ocolors[0] = colors[m];
					ocolors[1] = colors[m+1];
					ocolors[2] = colors[m+2];
				}
				else
				{
					colors[m] = ocolors[0];
					colors[m+1] = ocolors[1];
					colors[m+2] = ocolors[2];
				}

			}
			Cs = true;
			break;
		}
	}

	pieces = min(8, pieces);
	TqInt m;

	if (Cs)
	{
		for (i=0; i < pieces-1; i ++)
		{
			Aqsis::log() << info << "Creating RiPointsPolygons for piece " << i << "[" << pieces-1 << "]" << std::endl;
			m = (i * npolygons)/pieces;
			RiPointsPolygons(npolygons/pieces, nvertices, &vertices[3 * m], RI_P, points, RI_CS, colors, RI_NULL);
			Aqsis::log() << info << "Done creating RiPointsPolygons for piece " << i << std::endl;
		}

		Aqsis::log() << info << "Creating RiPointsPolygons for piece " << (pieces-1) << "[" << pieces-1 << "]" << std::endl;
		m = ((pieces-1) * npolygons) / pieces;
		TqInt nmax = npolygons - m;
		RiPointsPolygons(nmax, nvertices, &vertices[3 * m], RI_P, points, RI_CS, colors, RI_NULL);
		Aqsis::log() << info << "Done creating RiPointsPolygons for piece " << (pieces-1) << std::endl;
	}
	else
	{
		for (i=0; i < pieces-1; i ++)
		{
			Aqsis::log() << info << "Creating RiPointsPolygons for piece " << i << "[" << pieces-1 << "]" << std::endl;
			m = (i * npolygons)/pieces;
			RiPointsPolygons(npolygons/pieces, nvertices, &vertices[3 * m], RI_P, points, RI_NULL);
			Aqsis::log() << info << "Done creating RiPointsPolygons for piece " << i << std::endl;
		}

		Aqsis::log() << info << "Creating RiPointsPolygons for piece " << (pieces-1) << "[" << pieces-1 << "]" << std::endl;
		m = ((pieces-1) * npolygons) / pieces;
		TqInt nmax = npolygons - m;
		RiPointsPolygons(nmax, nvertices, &vertices[3 * m], RI_P, points, RI_NULL);
		Aqsis::log() << info << "Done creating RiPointsPolygons for piece " << (pieces-1) << std::endl;
	}

	Aqsis::log() << info << "Created RiPointsPolygons for Blobby" << std::endl;

	delete[] nvertices;
	delete[] vertices;
	delete[] points;
	delete[] colors;
}


//----------------------------------------------------------------------
/** Specify a small Points primitives
 *
 *\return	nothing
 **/
RtVoid RiCxxCore::Points(const ParamList& pList)
{
	RtInt npoints = countP(pList);
	// Create a storage class for all the points.
	boost::shared_ptr<CqPolygonPoints> pPointsClass( new CqPolygonPoints( npoints, 1, npoints ) );

	// Create a new points storage class
	boost::shared_ptr<CqPoints> pSurface;

	// read in the parameter list
	if ( ProcessPrimitiveVariables( pPointsClass.get(), pList ) )
	{
		// Transform the points into camera space for processing,
		// This needs to be done before initialising the KDTree as the tree must be formulated in 'current' (camera) space.
		CqMatrix matOtoW, matNOtoW, matVOtoW;
		QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pPointsClass->pTransform().get(), pPointsClass->pTransform() ->Time(0), matOtoW );
		QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pPointsClass->pTransform().get(), pPointsClass->pTransform() ->Time(0), matNOtoW );
		QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pPointsClass->pTransform().get(), pPointsClass->pTransform() ->Time(0), matVOtoW );
		pPointsClass->Transform( matOtoW, matNOtoW, matVOtoW);

		pSurface = boost::shared_ptr<CqPoints>( new CqPoints( npoints, pPointsClass ) );
		// Initialise the KDTree for the points to contain all.
		pSurface->InitialiseKDTree();
		pSurface->InitialiseMaxWidth();

		if ( QGetRenderContext() ->pattrCurrent() ->GetFloatAttribute( "System", "LODBound" ) [ 1 ] < 0.0f )
		{
			// Cull this geometry for LOD reasons
			return ;
		}

		/// \note:	Have to duplicate the work of CreateGPrim here as we need a special type of CqDeformingSurface.
		///			Not happy about this, need to look at it.
		// If in a motion block, confirm that the current deformation surface can accept the passed one as a keyframe.
		if( QGetRenderContext() ->pconCurrent() ->fMotionBlock() )
		{
			CqMotionModeBlock* pMMB = static_cast<CqMotionModeBlock*>(QGetRenderContext() ->pconCurrent().get());

			boost::shared_ptr<CqDeformingSurface> pMS = pMMB->GetDeformingSurface();
			// If this is the first frame, then generate the appropriate CqDeformingSurface and fill in the first frame.
			// Then cache the pointer on the motion block.
			if( !pMS )
			{
				boost::shared_ptr<CqDeformingPointsSurface> pNewMS( new CqDeformingPointsSurface( pSurface ) );
				pNewMS->AddTimeSlot( QGetRenderContext()->Time(), pSurface );

				pMMB->SetDeformingSurface( pNewMS );
			}
			else
			{
				pMS->AddTimeSlot( QGetRenderContext()->Time(), pSurface );
			}
			QGetRenderContext() ->AdvanceTime();
		}
		else
		{
			QGetRenderContext()->StorePrimitive( pSurface );
			STATS_INC( GPR_created );
		}
	}
}


//----------------------------------------------------------------------
// Specify small line primitives
RtVoid RiCxxCore::Curves(RtConstToken type, const IntArray& nvertices, RtConstToken wrap, const ParamList& pList)
{
	RtInt ncurves = nvertices.size();
	// find out whether the curve is periodic or non-periodic
	bool periodic = false;
	if ( strcmp( wrap, RI_PERIODIC ) == 0 )
	{
		periodic = true;
	}
	else if ( strcmp( wrap, RI_NONPERIODIC ) == 0 )
	{
		periodic = false;
	}
	else
	{
		// the wrap mode was neither "periodic" nor "nonperiodic"
		Aqsis::log() << error << "RiCurves invalid wrap mode \"" << wrap << "\"" << std::endl;
	}

	// handle creation of linear and cubic curve groups separately
	if ( strcmp( type, RI_CUBIC ) == 0 )
	{
		// create a new group of cubic curves
		boost::shared_ptr<CqCubicCurvesGroup> pSurface(
				new CqCubicCurvesGroup( ncurves, const_cast<TqInt*>(nvertices.begin()), periodic ) );
		// read in the parameter list
		if ( ProcessPrimitiveVariables( pSurface.get(), pList ) )
		{
			// set the default primitive variables
			pSurface->SetDefaultPrimitiveVariables();

			TqFloat time = QGetRenderContext()->Time();
			// Transform the points into camera space for processing,
			CqMatrix matOtoW, matNOtoW, matVOtoW;
			QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
			QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
			QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
			pSurface->Transform( matOtoW, matNOtoW, matVOtoW);

			CreateGPrim( pSurface );
		}
	}
	else if ( strcmp( type, RI_LINEAR ) == 0 )
	{
		// create a new group of linear curves
		boost::shared_ptr<CqLinearCurvesGroup> pSurface(
				new CqLinearCurvesGroup( ncurves, const_cast<TqInt*>(nvertices.begin()), periodic ) );

		// read in the parameter list
		if ( ProcessPrimitiveVariables( pSurface.get(), pList ) )
		{
			// set the default primitive variables
			pSurface->SetDefaultPrimitiveVariables();
			TqFloat time = QGetRenderContext()->Time();
			// Transform the points into camera space for processing,
			CqMatrix matOtoW, matNOtoW, matVOtoW;
			QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
			QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
			QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
			pSurface->Transform( matOtoW, matNOtoW, matVOtoW);
			CreateGPrim( pSurface );
		}
	}
	else
	{
		// the type of curve was neither "linear" nor "cubic"
		Aqsis::log() << error << "RiCurves invalid type \"" << type << "\"" << std::endl;
	}
}


//----------------------------------------------------------------------
// Specify a list of convex coplanar polygons and their shared vertices.
//
RtVoid RiCxxCore::PointsPolygons(const IntArray& nverts, const IntArray& verts, const ParamList& pList)
{
	RtInt npolys = nverts.size();
	// Calculate how many vertices there are.
	RtInt cVerts = 0;
	const RtInt* pVerts = verts.begin();
	RtInt poly;
	RtInt sumnVerts = 0;
	for ( poly = 0; poly < npolys; ++poly )
	{
		RtInt v;
		sumnVerts += nverts[ poly ];
		for ( v = 0; v < nverts[ poly ]; ++v )
		{
			cVerts = max( ( ( *pVerts ) + 1 ), cVerts );
			pVerts++;
		}
	}

	// Create a storage class for all the points.
	boost::shared_ptr<CqPolygonPoints> pPointsClass( new CqPolygonPoints( cVerts, npolys, sumnVerts ) );
	// Process any specified primitive variables
	if ( ProcessPrimitiveVariables( pPointsClass.get(), pList ) )
	{
		boost::shared_ptr<CqSurfacePointsPolygons> pPsPs(
				new CqSurfacePointsPolygons(pPointsClass, npolys, const_cast<TqInt*>(nverts.begin()),
											const_cast<TqInt*>(verts.begin()) ) );
		TqFloat time = QGetRenderContext()->Time();
		// Transform the points into camera space for processing,
		CqMatrix matOtoW, matNOtoW, matVOtoW;
		QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pPointsClass->pTransform().get(), time, matOtoW );
		QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pPointsClass->pTransform().get(), time, matNOtoW );
		QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pPointsClass->pTransform().get(), time, matVOtoW );
		pPointsClass->Transform( matOtoW, matNOtoW, matVOtoW);
		CreateGPrim(pPsPs);
	}
}


//----------------------------------------------------------------------
// Specify a list of coplanar, non-convex polygons and their shared vertices.
//
RtVoid RiCxxCore::PointsGeneralPolygons(const IntArray& nloops, const IntArray& nverts, const IntArray& verts, const ParamList& pList)
{
	RtInt npolys = nloops.size();

	TqUint ipoly;
	TqUint iloop;
	TqUint igloop = 0;
	TqInt cVerts = 0;
	TqUint igvert = 0;
	TqInt initial_index;
	TqInt sumnVerts = 0;

	// Calculate how many points overall.
	const RtInt* pVerts = verts.begin();
	for ( ipoly = 0; ipoly < (TqUint) npolys; ++ipoly )
	{
		for ( iloop = 0; iloop < (TqUint) nloops[ ipoly ]; ++iloop, ++igloop )
		{
			TqInt v;
			sumnVerts += nverts[ igloop ];
			// Check for degenerate loops.
			if( nverts[ igloop ] < 3 )
			{
				CqString objname( "unnamed" );
				const CqString* pattrName = QGetRenderContext()->pattrCurrent()->GetStringAttribute( "identifier", "name" );
				if ( pattrName != 0 )
					objname = pattrName[ 0 ];
				Aqsis::log() << warning << "Degenerate loop in PointsGeneralPolygons object \"" << objname.c_str() << "\"" << std::endl;
			}
			for ( v = 0; v < nverts[ igloop ]; ++v )
			{
				cVerts = max( ( ( *pVerts ) + 1 ), cVerts );
				pVerts++;
			}
		}
	}

	// We need to take into account Orientation here.
	bool O = QGetRenderContext()->pattrCurrent() ->GetIntegerAttribute( "System", "Orientation" ) [ 0 ] != 0;

	// Create a storage class for all the points.
	boost::shared_ptr<CqPolygonPoints> pPointsClass( new CqPolygonPoints( cVerts, npolys, sumnVerts ) );
	// Process any specified primitive variables
	if ( ProcessPrimitiveVariables( pPointsClass.get(), pList ) )
	{
		pPointsClass->SetDefaultPrimitiveVariables( RI_FALSE );

		// Reset loop counter.
		igloop = 0;
		TqUint ctris = 0;
		std::vector<TqInt>	aiTriangles;
		std::vector<TqInt> aFVList;
		std::vector<TqInt> aUVList;

		for ( ipoly = 0; ipoly < (TqUint) npolys; ++ipoly )
		{
			initial_index = igvert;
			// Create a general 2D polygon using the points in each loop.
			CqPolygonGeneral2D poly;
			TqUint ipoint = 0;
			TqUint imaxindex, iminindex;
			imaxindex = cVerts;
			iminindex = 0;
			for ( iloop = 0; iloop < (TqUint) nloops[ ipoly ]; ++iloop, ++igloop )
			{
				iminindex = min( iminindex, (TqUint) verts[ igvert ] );
				imaxindex = max( imaxindex, (TqUint) verts[ igvert ] );

				CqPolygonGeneral2D polya;
				polya.SetpVertices( pPointsClass );
				TqInt ivert;
				for ( ivert = 0; ivert < nverts[ igloop ]; ++ivert, ++igvert )
				{
					ipoint = verts[ igvert ];
					assert( ipoint < pPointsClass->P() ->Size() );
					polya.aiVertices().push_back( ipoint );
				}
				// Work out which plane to project onto.
				polya.CalcAxis();

				if ( iloop == 0 )
				{
					/// \note: We need to check here if the orientation of the projected poly matches the
					/// expected one, of not, we must swap the direction so that the triangulation routines can
					/// correctly determine the inside/outside nature of points. However, if doing so breaks the
					/// orientation as expected by the rest of the renderer, we need to flip the orientation
					/// attribute as well so that normals are correctly calculated.
					if( !O )
					{
						if ( polya.CalcOrientation() != CqPolygonGeneral2D::Orientation_Clockwise )
							polya.SwapDirection();
					}
					else
					{
						if ( polya.CalcOrientation() != CqPolygonGeneral2D::Orientation_AntiClockwise )
							polya.SwapDirection();
					}
					poly = polya;
				}
				else
				{
					if( !O )
					{
						if ( polya.CalcOrientation() != CqPolygonGeneral2D::Orientation_AntiClockwise )
							polya.SwapDirection();
					}
					else
					{
						if ( polya.CalcOrientation() != CqPolygonGeneral2D::Orientation_Clockwise )
							polya.SwapDirection();
					}
					poly.Combine( polya );
				}
			}
			// Now triangulate the general polygon

			poly.CalcOrientation();
			TqUint iStartTri = aiTriangles.size();
			poly.Triangulate( aiTriangles );
			TqUint iEndTri = aiTriangles.size();
			// Store the facevarying information
			/// \note This code relies on the fact that vertex indices cannot be duplicated
			/// within the loops of a single poly. Make sure this is a reasonable assumption.
			for( TqUint ifv = iStartTri; ifv < iEndTri; ++ifv )
			{
				TqInt ivaryingindex = aiTriangles[ ifv ];
				bool found = false;
				for( TqUint iv = initial_index; iv != igvert; ++iv )
				{
					if( verts[ iv ] == ivaryingindex )
					{
						aFVList.push_back( iv );
						found = true;
					}
				}
				assert( found );
			}

			// Store the count of triangles generated for this general poly, so that we
			// can duplicate up the uniform values as appropriate.
			/// \note This code relies on the fact that vertex indices cannot be duplicated
			/// within the loops of a single poly. Make sure this is a reasonable assumption.
			aUVList.push_back( ( iEndTri - iStartTri ) / 3 );
		}

		// Build an array of point counts (always 3 each).
		ctris = aiTriangles.size() / 3;
		std::vector<RtInt> _nverts;
		_nverts.resize( ctris, 3 );

		// Modified parameter list; rebuild any facevarying or uniform
		// variables.
		std::vector<Ri::Param> pListNew;

		TqInt fvcount = ctris * 3;
		assert( static_cast<TqInt>(aFVList.size()) == fvcount );
		std::vector<void*> aNewParams;
		for(size_t iUserParam = 0; iUserParam < pList.size(); ++iUserParam )
		{
			const Ri::TypeSpec& spec = pList[iUserParam].spec();
			int storCount = spec.storageCount();
			int elem_bytes = storCount;
			switch( spec.storageType() )
			{
				case Ri::TypeSpec::Float:   elem_bytes *= sizeof(RtFloat);       break;
				case Ri::TypeSpec::Integer: elem_bytes *= sizeof(RtInt);         break;
				case Ri::TypeSpec::String:  elem_bytes *= sizeof(RtConstString); break;
				case Ri::TypeSpec::Pointer: break;
				default: assert(0); break;
			}

			if( spec.iclass == Ri::TypeSpec::FaceVarying || spec.iclass == Ri::TypeSpec::FaceVertex )
			{
				char* pNew = static_cast<char*>( malloc( elem_bytes * fvcount) );
				aNewParams.push_back( pNew );
				TqInt iElem;
				for( iElem = 0; iElem < fvcount; ++iElem )
				{
					const unsigned char* pval = static_cast<const unsigned char*>( pList[iUserParam].data() ) + ( aFVList[ iElem ] * elem_bytes );
					memcpy( pNew, pval, ( elem_bytes ));
					pNew += elem_bytes;
				}
				pListNew.push_back(Ri::Param(pList[iUserParam].spec(), pList[iUserParam].name(), aNewParams.back(), fvcount*storCount));
			}
			else if( spec.iclass == Ri::TypeSpec::Uniform )
			{
				// Allocate enough for 1 value per triangle, then duplicate values from the original list
				// accordingly.
				char* pNew = static_cast<char*>( malloc( elem_bytes * ctris ) );
				aNewParams.push_back( pNew );
				TqInt iElem;
				const unsigned char* pval = static_cast<const unsigned char*>( pList[iUserParam].data() );
				for( iElem = 0; iElem < npolys; ++iElem )
				{
					TqInt dup_count = aUVList[ iElem ];
					TqInt dup;
					for(dup=0; dup < dup_count; dup++)
					{
						memcpy( pNew, pval, ( elem_bytes ));
						pNew += elem_bytes;
					}
					pval += elem_bytes;
				}
				pListNew.push_back(Ri::Param(pList[iUserParam].spec(), pList[iUserParam].name(), aNewParams.back(), ctris*storCount));
			}
			else
			{
				// everything else should just be copied through from pList.
				pListNew.push_back(pList[iUserParam]);
			}
		}

		PointsPolygons(IntArray(&_nverts[0], _nverts.size()),
					   IntArray(&aiTriangles[0], aiTriangles.size()),
					   ParamList(pListNew.empty()?0:&pListNew[0], pListNew.size()));

		std::vector<void*>::iterator iNewParam;
		for( iNewParam = aNewParams.begin(); iNewParam != aNewParams.end(); ++iNewParam )
			free( *iNewParam );
	}
}


//----------------------------------------------------------------------
// Specify the patch basis matrices for the u and v directions, and the knot skip values.
//
RtVoid RiCxxCore::Basis(RtConstBasis ubasis, RtInt ustep, RtConstBasis vbasis, RtInt vstep)
{
	CqMatrix u;
	CqMatrix v;

	// A good parser will use the Ri*Basis pointers so a quick comparison
	//   can be done.
	//if ( ubasis not same as before )
	//{
	//	// Save off the newly given basis.
	//
	//	// Calculate the (inverse Bezier Basis) * (given basis), but do
	//	//   a quick check for RiPowerBasis since that is an identity
	//	//   matrix requiring no math.
	//	if ( ubasis!=RiPowerBasis )
	//	{
	//	}
	//	else
	//	{
	//	}
	//
	// Do the above again for vbasis.
	// Save off (InvBezier * VBasis) and (Transpose(InvBezier*UBasis)).
	//}

	RtInt i;
	for ( i = 0; i < 4; ++i )
	{
		RtInt j;
		for ( j = 0; j < 4; ++j )
		{
			u.SetElement( i, j, ubasis[ i ][ j ] );
			v.SetElement( i, j, vbasis[ i ][ j ] );
		}
	}
	u.SetfIdentity( false );
	v.SetfIdentity( false );

	QGetRenderContext() ->pattrWriteCurrent() ->GetMatrixAttributeWrite( "System", "Basis" ) [ 0 ] = u;
	QGetRenderContext() ->pattrWriteCurrent() ->GetMatrixAttributeWrite( "System", "Basis" ) [ 1 ] = v;
	QGetRenderContext() ->pattrWriteCurrent() ->GetIntegerAttributeWrite( "System", "BasisStep" ) [ 0 ] = ustep;
	QGetRenderContext() ->pattrWriteCurrent() ->GetIntegerAttributeWrite( "System", "BasisStep" ) [ 1 ] = vstep;
	QGetRenderContext() ->AdvanceTime();
}


//----------------------------------------------------------------------
// Specify a new patch primitive.
//
RtVoid RiCxxCore::Patch(RtConstToken type, const ParamList& pList)
{
	if ( strcmp( type, RI_BICUBIC ) == 0 )
	{
		// Create a surface patch
		boost::shared_ptr<CqSurfacePatchBicubic> pSurface( new CqSurfacePatchBicubic() );
		// Fill in primitive variables specified.
		if ( ProcessPrimitiveVariables( pSurface.get(), pList ) )
		{
			// Fill in default values for all primitive variables not explicitly specified.
			pSurface->SetDefaultPrimitiveVariables();
			CqMatrix matuBasis = pSurface->pAttributes() ->GetMatrixAttribute( "System", "Basis" ) [ 0 ];
			CqMatrix matvBasis = pSurface->pAttributes() ->GetMatrixAttribute( "System", "Basis" ) [ 1 ];
			pSurface->ConvertToBezierBasis( matuBasis, matvBasis );

			TqFloat time = QGetRenderContext()->Time();
			// Transform the points into camera space for processing,
			CqMatrix matOtoW, matNOtoW, matVOtoW;
			QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
			QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
			QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
			pSurface->Transform( matOtoW, matNOtoW, matVOtoW);

			CreateGPrim( pSurface );
		}
	}
	else if ( strcmp( type, RI_BILINEAR ) == 0 )
	{
		// Create a surface patch
		boost::shared_ptr<CqSurfacePatchBilinear> pSurface( new CqSurfacePatchBilinear() );
		// Fill in primitive variables specified.
		if ( ProcessPrimitiveVariables( pSurface.get(), pList ) )
		{
			// Fill in default values for all primitive variables not explicitly specified.
			pSurface->SetDefaultPrimitiveVariables();
			TqFloat time = QGetRenderContext()->Time();
			// Transform the points into camera space for processing,
			CqMatrix matOtoW, matNOtoW, matVOtoW;
			QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
			QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
			QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
			pSurface->Transform( matOtoW, matNOtoW, matVOtoW);
			CreateGPrim( pSurface );
		}
	}
	else
	{
		Aqsis::log() << error << "RiPatch invalid patch type \"" << type << "\"" << std::endl;
	}
}


//----------------------------------------------------------------------
// Specify a quadrilaterla mesh of patches.
//

RtVoid RiCxxCore::PatchMesh(RtConstToken type, RtInt nu, RtConstToken uwrap, RtInt nv, RtConstToken vwrap, const ParamList& pList)
{
	if( strcmp( uwrap, RI_PERIODIC ) && strcmp( uwrap, RI_NONPERIODIC ) )
		Aqsis::log() << error << "RiPatchMesh invalid u-wrap type: \"" << uwrap << "\"" << std::endl;

	if( strcmp( vwrap, RI_PERIODIC ) && strcmp( vwrap, RI_NONPERIODIC ) )
		Aqsis::log() << error << "RiPatchMesh invalid v-wrap type: \"" << vwrap << "\"" << std::endl;

	if ( strcmp( type, RI_BICUBIC ) == 0 )
	{
		// Create a surface patch
		bool	uPeriodic = ( strcmp( uwrap, RI_PERIODIC ) == 0 ) ? true : false;
		bool	vPeriodic = ( strcmp( vwrap, RI_PERIODIC ) == 0 ) ? true : false;

		boost::shared_ptr<CqSurfacePatchMeshBicubic> pSurface( new CqSurfacePatchMeshBicubic( nu, nv, uPeriodic, vPeriodic ) );
		// Fill in primitive variables specified.
		if ( ProcessPrimitiveVariables( pSurface.get(), pList ) )
		{
			// Fill in default values for all primitive variables not explicitly specified.
			pSurface->SetDefaultPrimitiveVariables();
			TqFloat time = QGetRenderContext()->Time();
			// Convert to Bezier basis
			pSurface->ConvertToBezierBasis();
			// Transform into camera space for processing
			CqMatrix matOtoW, matNOtoW, matVOtoW;
			QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
			QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
			QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
			pSurface->Transform( matOtoW, matNOtoW, matVOtoW);
			CreateGPrim( pSurface );
		}
	}
	else if ( strcmp( type, RI_BILINEAR ) == 0 )
	{
		// Create a surface patch
		bool	uPeriodic = ( strcmp( uwrap, RI_PERIODIC ) == 0 ) ? true : false;
		bool	vPeriodic = ( strcmp( vwrap, RI_PERIODIC ) == 0 ) ? true : false;

		boost::shared_ptr<CqSurfacePatchMeshBilinear> pSurface( new CqSurfacePatchMeshBilinear( nu, nv, uPeriodic, vPeriodic ) );
		// Fill in primitive variables specified.
		if ( ProcessPrimitiveVariables( pSurface.get(), pList ) )
		{
			// Fill in default values for all primitive variables not explicitly specified.
			pSurface->SetDefaultPrimitiveVariables();
			TqFloat time = QGetRenderContext()->Time();
			// Transform the points into camera space for processing,
			CqMatrix matOtoW, matNOtoW, matVOtoW;
			QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
			QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
			QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
			pSurface->Transform( matOtoW, matNOtoW, matVOtoW);
			CreateGPrim( pSurface );
		}
	}
	else
	{
		Aqsis::log() << error << "RiPatchMesh invalid type \"" << type << "\"" << std::endl;
	}
}


//----------------------------------------------------------------------
// Specify a new non uniform patch.
//
RtVoid RiCxxCore::NuPatch(RtInt nu, RtInt uorder, const FloatArray& uknot, RtFloat umin, RtFloat umax, RtInt nv, RtInt vorder, const FloatArray& vknot, RtFloat vmin, RtFloat vmax, const ParamList& pList)
{
	// Create a NURBS patch
	boost::shared_ptr<CqSurfaceNURBS> pSurface( new CqSurfaceNURBS() );
	pSurface->SetfPatchMesh();
	pSurface->Init( uorder, vorder, nu, nv );

	pSurface->Setumin( umin );
	pSurface->Setumax( umax );
	pSurface->Setvmin( vmin );
	pSurface->Setvmax( vmax );

	// Copy the knot vectors.
	RtInt i;
	for ( i = 0; i < nu + uorder; ++i )
		pSurface->auKnots() [ i ] = uknot[ i ];
	for ( i = 0; i < nv + vorder; ++i )
		pSurface->avKnots() [ i ] = vknot[ i ];

	// Process any specified parameters
	if ( ProcessPrimitiveVariables( pSurface.get(), pList ) )
	{
		// Set up the default primitive variables.
		pSurface->SetDefaultPrimitiveVariables();
		// Clamp the surface to ensure non-periodic.
		pSurface->Clamp();
		TqFloat time = QGetRenderContext()->Time();
		// Transform the points into camera space for processing,
		CqMatrix matOtoW, matNOtoW, matVOtoW;
		QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
		QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
		QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
		pSurface->Transform( matOtoW, matNOtoW, matVOtoW);
		CreateGPrim( pSurface );
	}
}

//----------------------------------------------------------------------
// Specify curves which are used to trim NURBS surfaces.
//
RtVoid RiCxxCore::TrimCurve(const IntArray& ncurves, const IntArray& order, const FloatArray& knot, const FloatArray& min, const FloatArray& max, const IntArray& n, const FloatArray& u, const FloatArray& v, const FloatArray& w)
{
	RtInt nloops = ncurves.size();
	// Clear the current loop array.
	QGetRenderContext() ->pattrWriteCurrent() ->TrimLoops().Clear();

	// Build an array of curves per specified loop.
	TqInt in = 0;
	TqInt iorder = 0;
	TqInt iknot = 0;
	TqInt ivert = 0;
	TqInt iloop;

	for ( iloop = 0; iloop < nloops; ++iloop )
	{
		CqTrimLoop Loop;
		TqInt icurve;
		for ( icurve = 0; icurve < ncurves[ iloop ]; ++icurve )
		{
			// Create a NURBS patch
			CqTrimCurve Curve;
			TqInt o = order[ iorder++ ];
			TqInt cverts = n[ in++ ];
			Curve.Init( o, cverts );

			// Copy the knot vectors.
			RtInt i;
			for ( i = 0; i < o + cverts; ++i )
				Curve.aKnots() [ i ] = knot[ iknot++ ];

			// Copy the vertices from the u,v,w arrays.
			CqVector3D vec( 0, 0, 1 );
			for ( i = 0; i < cverts; ++i )
			{
				vec.x( u[ ivert ] );
				vec.y( v[ ivert ] );
				vec.z( w[ ivert++ ] );
				Curve.CP( i ) = vec;
			}
			Loop.aCurves().push_back( Curve );
		}
		QGetRenderContext() ->pattrWriteCurrent() ->TrimLoops().aLoops().push_back( Loop );
	}
}


//----------------------------------------------------------------------
// Specify a sphere primitive.
//
RtVoid RiCxxCore::Sphere(RtFloat radius, RtFloat zmin, RtFloat zmax, RtFloat thetamax, const ParamList& pList)
{
	// Create a sphere
	boost::shared_ptr<CqSphere> pSurface( new CqSphere( radius, zmin, zmax, 0, thetamax ) );
	ProcessPrimitiveVariables( pSurface.get(), pList );
	pSurface->SetDefaultPrimitiveVariables();

	TqFloat time = QGetRenderContext()->Time();
	// Transform the points into camera space for processing,
	CqMatrix matOtoW, matNOtoW, matVOtoW;
	QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
	QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
	QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
	pSurface->Transform( matOtoW, matNOtoW, matVOtoW);
	CreateGPrim( pSurface );
}


//----------------------------------------------------------------------
// Specify a cone primitive.
//
RtVoid RiCxxCore::Cone(RtFloat height, RtFloat radius, RtFloat thetamax, const ParamList& pList)
{
	/// \note This should be an exception and get caught further up.
	if( thetamax == 0 )
		return;

	// Create a cone
	boost::shared_ptr<CqCone> pSurface( new CqCone( height, radius, 0, thetamax, 0, 1.0f ) );
	ProcessPrimitiveVariables( pSurface.get(), pList );
	pSurface->SetDefaultPrimitiveVariables();

	TqFloat time = QGetRenderContext()->Time();
	// Transform the points into camera space for processing,
	CqMatrix matOtoW, matNOtoW, matVOtoW;
	QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
	QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
	QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
	pSurface->Transform( matOtoW, matNOtoW, matVOtoW);
	CreateGPrim( pSurface );
}


//----------------------------------------------------------------------
// Specify a culinder primitive.
//
RtVoid RiCxxCore::Cylinder(RtFloat radius, RtFloat zmin, RtFloat zmax, RtFloat thetamax, const ParamList& pList)
{
	// Create a cylinder
	boost::shared_ptr<CqCylinder> pSurface( new CqCylinder( radius, zmin, zmax, 0, thetamax ) );
	ProcessPrimitiveVariables( pSurface.get(), pList );
	pSurface->SetDefaultPrimitiveVariables();

	TqFloat time = QGetRenderContext()->Time();
	// Transform the points into camera space for processing,
	CqMatrix matOtoW, matNOtoW, matVOtoW;
	QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
	QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
	QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
	pSurface->Transform( matOtoW, matNOtoW, matVOtoW);
	CreateGPrim( pSurface );
}


//----------------------------------------------------------------------
// Specify a hyperboloid primitive.
//
RtVoid RiCxxCore::Hyperboloid(RtConstPoint point1, RtConstPoint point2, RtFloat thetamax, const ParamList& pList)
{
	// Create a hyperboloid
	CqVector3D v0( point1[ 0 ], point1[ 1 ], point1[ 2 ] );
	CqVector3D v1( point2[ 0 ], point2[ 1 ], point2[ 2 ] );
	boost::shared_ptr<CqHyperboloid> pSurface( new CqHyperboloid( v0, v1, 0, thetamax ) );
	ProcessPrimitiveVariables( pSurface.get(), pList );
	pSurface->SetDefaultPrimitiveVariables();

	TqFloat time = QGetRenderContext()->Time();
	// Transform the points into camera space for processing,
	CqMatrix matOtoW, matNOtoW, matVOtoW;
	QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
	QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
	QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
	pSurface->Transform( matOtoW, matNOtoW, matVOtoW);
	CreateGPrim( pSurface );
}


//----------------------------------------------------------------------
// Specify a paraboloid primitive.
//
RtVoid RiCxxCore::Paraboloid(RtFloat rmax, RtFloat zmin, RtFloat zmax, RtFloat thetamax, const ParamList& pList)
{
	// Create a paraboloid
	boost::shared_ptr<CqParaboloid> pSurface( new CqParaboloid( rmax, zmin, zmax, 0, thetamax ) );
	ProcessPrimitiveVariables( pSurface.get(), pList );
	pSurface->SetDefaultPrimitiveVariables();

	TqFloat time = QGetRenderContext()->Time();
	// Transform the points into camera space for processing,
	CqMatrix matOtoW, matNOtoW, matVOtoW;
	QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
	QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
	QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
	pSurface->Transform( matOtoW, matNOtoW, matVOtoW);
	CreateGPrim( pSurface );
}


//----------------------------------------------------------------------
// Specify a disk primitive.
//
RtVoid RiCxxCore::Disk(RtFloat height, RtFloat radius, RtFloat thetamax, const ParamList& pList)
{
	// Create a disk
	boost::shared_ptr<CqDisk> pSurface( new CqDisk( height, 0, radius, 0, thetamax ) );
	ProcessPrimitiveVariables( pSurface.get(), pList );
	pSurface->SetDefaultPrimitiveVariables();

	TqFloat time = QGetRenderContext()->Time();
	// Transform the points into camera space for processing,
	CqMatrix matOtoW, matNOtoW, matVOtoW;
	QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
	QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
	QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
	pSurface->Transform( matOtoW, matNOtoW, matVOtoW);

	CreateGPrim( pSurface );
}


//----------------------------------------------------------------------
// Specify a torus primitive.
//
RtVoid RiCxxCore::Torus(RtFloat majorrad, RtFloat minorrad, RtFloat phimin, RtFloat phimax, RtFloat thetamax, const ParamList& pList)
{
	// Create a torus
	boost::shared_ptr<CqTorus> pSurface( new CqTorus( majorrad, minorrad, phimin, phimax, 0, thetamax ) );
	ProcessPrimitiveVariables( pSurface.get(), pList );
	pSurface->SetDefaultPrimitiveVariables();

	TqFloat time = QGetRenderContext()->Time();
	// Transform the points into camera space for processing,
	CqMatrix matOtoW, matNOtoW, matVOtoW;
	QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
	QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
	QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
	pSurface->Transform( matOtoW, matNOtoW, matVOtoW);

	CreateGPrim( pSurface );
}


//----------------------------------------------------------------------
// Implement the procedural type primitive.
//
RtVoid RiCxxCore::Procedural(RtPointer data, RtConstBound bound, RtProcSubdivFunc refineproc, RtProcFreeFunc freeproc)
{
	CqBound B(bound);
	boost::shared_ptr<CqProcedural> pProc( new CqProcedural(data, B, refineproc, freeproc ) );
	TqFloat time = QGetRenderContext()->Time();
	CqMatrix matOtoW, matNOtoW, matVOtoW;
	QGetRenderContext()->matSpaceToSpace( "object", "world", NULL, pProc->pTransform().get(), time, matOtoW );
	QGetRenderContext()->matNSpaceToSpace( "object", "world", NULL, pProc->pTransform().get(), time, matNOtoW );
	QGetRenderContext()->matVSpaceToSpace( "object", "world", NULL, pProc->pTransform().get(), time, matVOtoW );
	pProc->Transform( matOtoW, matNOtoW, matVOtoW);
	CreateGPrim( pProc );
}



//----------------------------------------------------------------------
// Specify a special primitive.
//
RtVoid RiCxxCore::Geometry(RtConstToken type, const ParamList& pList)
{
	if ( strcmp( type, "teapot" ) == 0 )
	{

		// Create a standard teapot
		boost::shared_ptr<CqTeapot> pSurface( new CqTeapot( true ) ); // add a bottom if true/false otherwise

		pSurface->SetSurfaceParameters( *pSurface );
		ProcessPrimitiveVariables( pSurface.get(), pList );
		pSurface->SetDefaultPrimitiveVariables();

		// I don't use the original teapot primitives as defined by T. Burge
		// but an array of Patch Bicubic (stolen from example from Pixar) and
		// those (6 meshes) are registered as standards GPrims right here.
		// Basically I kept the bound, transform and split, dice and diceable methods
		// in teapot.cpp but I suspect they are never called since the work of
		// dicing will rely on the registered Gprimitives (see below in the for loop).
		// I suspect the 6/7 meshes are equivalent in size/definition as the T. Burge
		// definition. The 7th is the bottom part of the teapot (see teapot.cpp).

		for ( int i = 0; i < pSurface->cNbrPatchMeshBicubic; ++i )
		{
			boost::shared_ptr<CqSurface> pMesh = pSurface->pPatchMeshBicubic[ i ];

			TqFloat time = QGetRenderContext()->Time();
			// Transform the points into camera space for processing,
			CqMatrix matOtoW, matNOtoW, matVOtoW;
			QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
			QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
			QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
			pMesh->Transform( matOtoW, matNOtoW, matVOtoW);

			CreateGPrim( boost::static_pointer_cast<CqSurface>( pMesh ) );
		}
	}
	else if ( strcmp( type, "sphere" ) == 0 )
	{
		// \todo <b>Code Review</b> Do we really need this when we have RiSphere?
		// Create a sphere
		boost::shared_ptr<CqSphere> pSurface( new CqSphere( 1, -1, 1, 0, 360.0 ) );
		ProcessPrimitiveVariables( pSurface.get(), pList );
		pSurface->SetDefaultPrimitiveVariables();

		TqFloat time = QGetRenderContext()->Time();
		// Transform the points into camera space for processing,
		CqMatrix matOtoW, matNOtoW, matVOtoW;
		QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matOtoW );
		QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matNOtoW );
		QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pSurface->pTransform().get(), time, matVOtoW );
		pSurface->Transform( matOtoW, matNOtoW, matVOtoW);

		CreateGPrim( pSurface );
	} else if ( strcmp( type, "bunny" ) == 0 )
	{
	CqBunny bunny;

	std::vector<RtToken> aTokens;
	std::vector<RtPointer> aValues;
	std::vector<RtToken> aTags;
	aTokens.clear();
	aValues.clear();
	aTags.clear();


	aTokens.push_back(RI_P);
	aTokens.push_back(RI_S);
	aTokens.push_back(RI_T);
	aValues.push_back(bunny.Points());
	aValues.push_back(bunny.S());
	aValues.push_back(bunny.T());
	aTags.push_back(tokenCast("catmull-clark"));
	aTags.push_back(tokenCast("interpolateboundary"));

	static TqInt params[] = { 0, 0 };

	TqInt count = 3;
	// Rotate and scale to match the teapot geometry
	RiAttributeBegin();
	RiTranslate(0.0, 0.0, 2.5);
	RiRotate(90.0, 1.0, 0.0, 0.0);
	RiScale(1.0/30.0, 1.0/30.0, 1.0/30.0);
	RiSubdivisionMeshV( aTags[0],
	                    bunny.NFaces(),
	                    bunny.Faces(),
	                    bunny.Indexes(),
	                    1,
	                    &aTags[1],
	                    params,
	                    0,
	                    0,
	                    count,
	                    aTokens.size()>0?&aTokens[0]:0,
	                    aValues.size()>0?&aValues[0]:0 );
	RiAttributeEnd();

	} else
	{
		Aqsis::log() << warning << "RiGeometry unrecognised type \"" << type << "\"" << std::endl;
	}
}

//----------------------------------------------------------------------
// Begin the definition of a CSG object.
//
RtVoid RiCxxCore::SolidBegin(RtConstToken type)
{
	CqString strType( type );
	QGetRenderContext() ->BeginSolidModeBlock( strType );
}


//----------------------------------------------------------------------
// End the definition of a CSG object.
//
RtVoid RiCxxCore::SolidEnd()
{
	QGetRenderContext() ->EndSolidModeBlock();
}


//----------------------------------------------------------------------
// Object retention and instancing.
RtVoid RiCxxCore::ObjectBegin(RtConstToken name)
{
	errorHandler().error(EqE_Bug, "ObjectBegin should be handled by a filter");
}
RtVoid RiCxxCore::ObjectEnd()
{
	errorHandler().error(EqE_Bug, "ObjectEnd should be handled by a filter");
}
RtVoid RiCxxCore::ObjectInstance(RtConstToken name)
{
	errorHandler().error(EqE_Bug,
						 "ObjectInstance should be handled by a filter");
}


//----------------------------------------------------------------------
// Begin the definition of the motion of an object for use by motion blur.
//
RtVoid RiCxxCore::MotionBegin(const FloatArray& times)
{
	QGetRenderContext() ->BeginMotionModeBlock( times.size(), const_cast<TqFloat*>(times.begin()) );
}


//----------------------------------------------------------------------
// End the definition of the motion of an object.
//
RtVoid RiCxxCore::MotionEnd()
{
	QGetRenderContext() ->EndMotionModeBlock();
}


//----------------------------------------------------------------------
// Convert a picture to a texture.
RtVoid RiCxxCore::MakeTexture(RtConstString imagefile, RtConstString texturefile, RtConstToken swrap, RtConstToken twrap, RtFilterFunc filterfunc, RtFloat swidth, RtFloat twidth, const ParamList& pList)
{
	AQSIS_TIME_SCOPE(Make_texture);

	SqWrapModes wrapModes(enumCast<EqWrapMode>(swrap), enumCast<EqWrapMode>(twrap));
	boost::filesystem::path inFileName
		= QGetRenderContext()->poptCurrent()->findRiFile(imagefile, "texture");
	makeTexture(inFileName, texturefile, SqFilterInfo(filterfunc, swidth, twidth),
			wrapModes, pList);
}


//----------------------------------------------------------------------
// Convert a picture to an environment map.
//
RtVoid RiCxxCore::MakeLatLongEnvironment(RtConstString imagefile, RtConstString reflfile, RtFilterFunc filterfunc, RtFloat swidth, RtFloat twidth, const ParamList& pList)
{
	assert(imagefile != 0 && reflfile != 0 && filterfunc != 0);

	AQSIS_TIME_SCOPE(Make_texture);

	boost::filesystem::path inFileName = QGetRenderContext()->poptCurrent()
		->findRiFile(imagefile, "texture");
	makeLatLongEnvironment(inFileName, reflfile, SqFilterInfo(filterfunc,
				swidth, twidth), pList);
}


//----------------------------------------------------------------------
// Convert a picture to a cubical environment map.
//
RtVoid RiCxxCore::MakeCubeFaceEnvironment(RtConstString px, RtConstString nx, RtConstString py, RtConstString ny, RtConstString pz, RtConstString nz, RtConstString reflfile, RtFloat fov, RtFilterFunc filterfunc, RtFloat swidth, RtFloat twidth, const ParamList& pList)
{
	assert( px != 0 && nx != 0 && py != 0 && ny != 0 && pz != 0 && nz != 0 &&
	        reflfile != 0 && filterfunc != 0 );




	AQSIS_TIME_SCOPE(Make_texture);

	const IqOptions& opts = *QGetRenderContext()->poptCurrent();

	makeCubeFaceEnvironment(
		opts.findRiFile(px, "texture"), opts.findRiFile(nx, "texture"),
		opts.findRiFile(py, "texture"), opts.findRiFile(ny, "texture"),
		opts.findRiFile(pz, "texture"), opts.findRiFile(nz, "texture"),
		reflfile, fov, SqFilterInfo(filterfunc, swidth, twidth),
		pList
	);
}


//----------------------------------------------------------------------
// Convert a depth map file to a shadow map.
//
RtVoid RiCxxCore::MakeShadow(RtConstString picfile, RtConstString shadowfile, const ParamList& pList)
{
	assert(picfile != 0 && shadowfile != 0);

	AQSIS_TIME_SCOPE(Make_texture);

	boost::filesystem::path inFileName = QGetRenderContext()->poptCurrent()
		->findRiFile(picfile, "texture");
	makeShadow(inFileName, shadowfile, pList);
}


//----------------------------------------------------------------------
// Convert a series of depth maps to an occlusion map.
//
RtVoid RiCxxCore::MakeOcclusion(const StringArray& picfiles, RtConstString shadowfile, const ParamList& pList)
{
	AQSIS_TIME_SCOPE(Make_texture);

	std::vector<boost::filesystem::path> fileNames;
	fileNames.reserve(picfiles.size());
	for(size_t i = 0; i < picfiles.size(); ++i)
	{
		fileNames.push_back( QGetRenderContext()->poptCurrent()
			->findRiFile(picfiles[i], "texture") );
	}
	makeOcclusion(fileNames, shadowfile, pList);
}

//----------------------------------------------------------------------
/** Conditional handlers for 3.4 conditional RIB */
RtVoid RiCxxCore::IfBegin(RtConstString condition)
{
	errorHandler().error(EqE_Bug, "IfBegin should be handled by utility filter");
}

RtVoid RiCxxCore::ElseIf(RtConstString condition)
{
	errorHandler().error(EqE_Bug, "ElseIf should be handled by utility filter");
}

RtVoid RiCxxCore::Else()
{
	errorHandler().error(EqE_Bug, "Else should be handled by utility filter");
}

RtVoid RiCxxCore::IfEnd()
{
	errorHandler().error(EqE_Bug, "IfEnd should be handled by utility filter");
}

//----------------------------------------------------------------------
// Set the function used to report errors.
//
RtVoid RiCxxCore::ErrorHandler(RtErrorFunc handler)
{
	QGetRenderContext()->SetpErrorHandler( handler );
}


//----------------------------------------------------------------------
// Specify a subdivision surface hull with tagging.
//
RtVoid RiCxxCore::SubdivisionMesh(RtConstToken scheme, const IntArray& nvertices, const IntArray& vertices, const TokenArray& tags, const IntArray& nargs, const IntArray& intargs, const FloatArray& floatargs, const ParamList& pList)
{
	RtInt nfaces = nvertices.size();
	RtInt ntags = tags.size();
	// Calculate how many vertices there are.
	RtInt cVerts = 0;
	const RtInt* pVerts = vertices.begin();
	RtInt face;
	RtInt sumnVerts = 0;
	for ( face = 0; face < nfaces; ++face )
	{
		RtInt v;
		sumnVerts += nvertices[ face ];
		for ( v = 0; v < nvertices[ face ]; ++v )
		{
			cVerts = max( ( ( *pVerts ) + 1 ), cVerts );
			pVerts++;
		}
	}

	// Create a storage class for all the points.
	boost::shared_ptr<CqPolygonPoints> pPointsClass( new CqPolygonPoints( cVerts, nfaces, sumnVerts ) );

	std::vector<boost::shared_ptr<CqPolygonPoints> >	apPoints;
	// Process any specified primitive variables
	if ( ProcessPrimitiveVariables( pPointsClass.get(), pList ) )
	{
		// Create experimental version
		if ( strcmp( scheme, "catmull-clark" ) == 0 )
		{
			// Transform the points into camera space for processing,
			TqFloat time = QGetRenderContext()->Time();
			CqMatrix matOtoW, matNOtoW, matVOtoW;
			QGetRenderContext() ->matSpaceToSpace( "object", "world", NULL, pPointsClass->pTransform().get(), time, matOtoW );
			QGetRenderContext() ->matNSpaceToSpace( "object", "world", NULL, pPointsClass->pTransform().get(), time, matNOtoW );
			QGetRenderContext() ->matVSpaceToSpace( "object", "world", NULL, pPointsClass->pTransform().get(), time, matVOtoW );
			pPointsClass->Transform( matOtoW, matNOtoW, matVOtoW);

			boost::shared_ptr<CqSubdivision2> pSubd2( new CqSubdivision2( pPointsClass ) );
			pSubd2->Prepare( cVerts );

			boost::shared_ptr<CqSurfaceSubdivisionMesh> pMesh( new CqSurfaceSubdivisionMesh(pSubd2, nfaces ) );

			RtInt	iP = 0;
			for ( face = 0; face < nfaces; ++face )
			{
				pSubd2->AddFacet( nvertices[face], const_cast<TqInt*>(&vertices[iP]), iP );
				iP += nvertices[ face ];
			}
			if ( pSubd2->Finalise() )
			{
				// Process tags.
				TqInt argcIndex = 0;
				TqInt floatargIndex = 0;
				TqInt intargIndex = 0;
				for ( TqInt i = 0; i < ntags; ++i )
				{
					if ( strcmp( tags[ i ], "interpolateboundary" ) == 0 )
						pSubd2->SetInterpolateBoundary( true );
					else if ( strcmp( tags [ i ], "crease" ) == 0 )
					{
						TqFloat creaseSharpness = floatargs[ floatargIndex ];
						// convert pixars 0->infinity crease values to our 0->1
						if( creaseSharpness > 5.0f )
							creaseSharpness = 5.0f;
						creaseSharpness /= 5.0f;
						// bend the curve so values behave more like pixars algorithm
						creaseSharpness = pow(creaseSharpness, 0.2f);
						TqInt iEdge = 0;
						while ( iEdge < nargs[ argcIndex ] - 1 )
						{
							if ( intargs[ iEdge + intargIndex ] < pSubd2->cVertices() &&
							        intargs[ iEdge + intargIndex + 1 ] < pSubd2->cVertices() )
							{
								// Store the sharp edge information in the top level mesh.
								pMesh->AddSharpEdge(intargs[ iEdge + intargIndex ], intargs[ iEdge + intargIndex + 1 ], creaseSharpness);
								// Store the crease sharpness.
								CqLath* pEdge = pSubd2->pVertex( intargs[ iEdge + intargIndex ] );
								std::vector<CqLath*> aQve;
								pEdge->Qve( aQve );
								std::vector<CqLath*>::iterator iOpp;
								for( iOpp = aQve.begin(); iOpp != aQve.end(); ++iOpp )
								{
									if( ( NULL != (*iOpp)->ec() ) && (*iOpp)->ec()->VertexIndex() == intargs[ iEdge + intargIndex + 1 ] )
									{
										pSubd2->AddSharpEdge( (*iOpp), creaseSharpness );
										pSubd2->AddSharpEdge( (*iOpp)->ec(), creaseSharpness );
										break;
									}
								}
							}
							iEdge++;
						}
					}
					else if ( strcmp( tags [ i ], "corner" ) == 0 )
					{
						TqInt iVertex = 0;
						while ( iVertex < nargs[ argcIndex ] )
						{
							if ( intargs[ iVertex + intargIndex ] < pSubd2->cVertices() )
							{
								// Store the sharp edge information in the top level mesh.
								pMesh->AddSharpCorner(intargs[ iVertex + intargIndex ], RI_INFINITY);
								// Store the corner sharpness.
								CqLath* pVertex = pSubd2->pVertex( intargs[ iVertex + intargIndex ] );
								pSubd2->AddSharpCorner( pVertex, RI_INFINITY );
							}
							iVertex++;
						}
					}
					else if ( strcmp( tags [ i ], "hole" ) == 0 )
					{
						TqInt iFace = 0;
						while ( iFace < nargs[ argcIndex ] )
						{
							pSubd2->SetHoleFace( intargs[ iFace + intargIndex ] );
							iFace++;
						}
					}

					intargIndex += nargs[ argcIndex++ ];
					floatargIndex += nargs[ argcIndex++ ];
				}

				CreateGPrim(pMesh);
			}
			else
			{
				Aqsis::log() << error << "RiSubdivisionMesh contains non-manifold data" << std::endl;
			}
		}
		else
		{
			Aqsis::log() << error << "RiSubdivisionMesh invalid scheme \"" << scheme << "\"" << std::endl;
		}
	}
}


RtVoid RiCxxCore::ReadArchive(RtConstToken name, RtArchiveCallback callback, const ParamList& pList)
{
	// Open the archive file
	boost::filesystem::ifstream archiveFile(
			QGetRenderContext()->poptCurrent()->findRiFile(name, "archive"),
			std::ios::binary);
	// Parse the archive
	RtArchiveCallback savedCallback = m_archiveCallback;
	m_archiveCallback = callback;
	m_apiServices.parseRib(archiveFile, name);
	m_archiveCallback = savedCallback;
}

RtVoid RiCxxCore::ArchiveBegin(RtConstToken name, const ParamList& pList)
{
	errorHandler().error(EqE_Bug, "ArchiveBegin should be handled by a filter");
}

RtVoid RiCxxCore::ArchiveEnd()
{
	errorHandler().error(EqE_Bug, "ArchiveEnd should have been handled by a filter");
}


RtVoid RiCxxCore::ClippingPlane(RtFloat x, RtFloat y, RtFloat z, RtFloat nx, RtFloat ny, RtFloat nz)
{
	errorHandler().warning(EqE_Unimplement, "Ignoring unimplemented interface call: RiClippingPlane");
}


//----------------------------------------------------------------------
//
RtVoid RiCxxCore::Resource(RtConstToken handle, RtConstToken type, const ParamList& pList)
{
	return;
}


//----------------------------------------------------------------------
//
RtVoid RiCxxCore::ResourceBegin()
{
}


//----------------------------------------------------------------------
//
RtVoid RiCxxCore::ResourceEnd()
{
}


RtVoid RiCxxCore::ShaderLayer(RtConstToken type, RtConstToken name, RtConstToken layername, const ParamList& pList)
{
	// If the current shader for the specified type is already a layer container, add this layer to it, if not,
	// create one and add this layer as the first.

	boost::shared_ptr<IqShader> newlayer;
	boost::shared_ptr<IqShader> layeredshader;
	CqString stringtype(type);
	stringtype = stringtype.ToLower();
	if(stringtype.compare("surface")==0)
	{
		newlayer = QGetRenderContext()->CreateShader( name, Type_Surface );
		layeredshader = QGetRenderContext()->pattrCurrent()->pshadSurface(QGetRenderContext()->Time());

		if( !layeredshader || !layeredshader->IsLayered() )
		{
			// Create a new layered shader and add this shader to it.
			layeredshader = boost::shared_ptr<IqShader>(new CqLayeredShader);
			layeredshader->SetTransform( QGetRenderContext() ->ptransCurrent() );
			QGetRenderContext() ->pattrWriteCurrent() ->SetpshadSurface( layeredshader, QGetRenderContext() ->Time() );
		}
	}
	else if(stringtype.compare("displacement")==0)
	{
		newlayer = QGetRenderContext()->CreateShader( name, Type_Displacement );
		layeredshader = QGetRenderContext()->pattrCurrent()->pshadDisplacement(QGetRenderContext()->Time());

		if( !layeredshader || !layeredshader->IsLayered() )
		{
			// Create a new layered shader and add this shader to it.
			layeredshader = boost::shared_ptr<IqShader>(new CqLayeredShader);
			layeredshader->SetTransform( QGetRenderContext() ->ptransCurrent() );
			QGetRenderContext() ->pattrWriteCurrent() ->SetpshadDisplacement( layeredshader, QGetRenderContext() ->Time() );
		}
	}
	else if(stringtype.compare("imager")==0)
	{
		newlayer = QGetRenderContext()->CreateShader( name, Type_Imager );
		layeredshader = QGetRenderContext()->poptCurrent()->pshadImager();

		if( !layeredshader || !layeredshader->IsLayered() )
		{
			// Create a new layered shader and add this shader to it.
			layeredshader = boost::shared_ptr<IqShader>(new CqLayeredShader);
			layeredshader->SetTransform( QGetRenderContext() ->ptransCurrent() );
			QGetRenderContext() ->poptWriteCurrent()->SetpshadImager( layeredshader );
		}
	}
	else
		Aqsis::log() << error << "Layered shaders not supported for type \"" << type << "\"" << std::endl;

	if ( newlayer && layeredshader )
	{
		newlayer->SetTransform( QGetRenderContext() ->ptransCurrent() );

		// Just add this layer in
		layeredshader->AddLayer(layername, newlayer);

		// Just check that the transformation hasn't changed between layers, as this is not handled.
		if(newlayer->matCurrent() != layeredshader->matCurrent())
			Aqsis::log() << error << "The shader space has changed between layers, this is not supported" << std::endl;

		// Execute the intiialisation code here, as we now have our shader context complete.
		newlayer->PrepareDefArgs();
		setShaderArguments(newlayer, pList);
		const TqInt* pMultipass = QGetRenderContext()->poptCurrent()->GetIntegerOption("Render", "multipass");
		if(pMultipass && !pMultipass[0])
			newlayer->PrepareShaderForUse();
	}
}

RtVoid RiCxxCore::ConnectShaderLayers(RtConstToken type, RtConstToken layer1, RtConstToken variable1, RtConstToken layer2, RtConstToken variable2)
{
	// If the current shader for the specified type is a layer container, add this connection to it
	CqString stringtype(type);
	stringtype = stringtype.ToLower();
	boost::shared_ptr<IqShader> pcurr;
	if(stringtype.compare("surface")==0)
		pcurr = QGetRenderContext()->pattrWriteCurrent()->pshadSurface(QGetRenderContext()->Time());
	else if(stringtype.compare("displacement")==0)
		pcurr = QGetRenderContext()->pattrWriteCurrent()->pshadDisplacement(QGetRenderContext()->Time());
	else if(stringtype.compare("imager")==0)
		pcurr = QGetRenderContext()->poptCurrent()->pshadImager();
	else
		Aqsis::log() << error << "Layered shaders not supported for type \"" << type << "\"" << std::endl;
	if( pcurr && pcurr->IsLayered() )
	{
		// Just add this layer in
		pcurr->AddConnection(layer1, variable1, layer2, variable2);
	}
}


//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Helper functions

//----------------------------------------------------------------------
// ProcessPrimitiveVariables
// Process and fill in any primitive variables.
// return	:	RI_TRUE if position specified, RI_FALSE otherwise.
static RtBoolean ProcessPrimitiveVariables(CqSurface * pSurface,
										   const Ri::ParamList& pList)
{
	std::vector<TqInt>	aUserParams;

	enum RIL_POINTS
	{
		RIL_NONE = -1,
		RIL_P,
		RIL_Pz,
		RIL_Pw,
	};

	// Read recognised parameter values.
	RtInt	fP = RIL_NONE;

	RtFloat*	pPoints = 0;

	for (size_t i = 0; i < pList.size(); ++i)
	{
		const void* value = pList[i].data();
		const Ri::TypeSpec& spec = pList[i].spec();
		const char* name = pList[i].name();
		bool isVertex = spec.iclass == Ri::TypeSpec::Vertex;
		if(!strcmp(name, RI_P) && isVertex)
		{
			fP = RIL_P;
			pPoints = ( RtFloat* ) value;
		}
		else if(!strcmp(name, RI_PZ) && isVertex)
		{
			fP = RIL_Pz;
			pPoints = ( RtFloat* ) value;
		}
		else if(!strcmp(name, RI_PW) && isVertex)
		{
			fP = RIL_Pw;
			pPoints = ( RtFloat* ) value;
		}
		else
		{
			aUserParams.push_back( i );
		}
	}

	// Fill in the position variable according to type.
	if ( fP != RIL_NONE )
	{
		CqParameterTypedVertex<CqVector4D, type_hpoint, CqVector3D>* P
			= new CqParameterTypedVertex<CqVector4D, type_hpoint, CqVector3D>("P", 1);
		TqInt cVertex = pSurface->cVertex();
		P->SetSize(cVertex);
		switch ( fP )
		{
			case RIL_P:
				for (TqInt i = 0; i < cVertex; ++i )
					P->pValue( i )[0] = CqVector4D( pPoints[ ( i * 3 ) ], pPoints[ ( i * 3 ) + 1 ], pPoints[ ( i * 3 ) + 2 ] );
				break;

			case RIL_Pz:
				for (TqInt i = 0; i < cVertex; ++i )
				{
					CqVector3D vecP = pSurface->SurfaceParametersAtVertex( i );
					vecP.z( pPoints[ i ] );
					P->pValue( i )[0] = vectorCast<CqVector4D>(vecP);
				}
				break;

			case RIL_Pw:
				for (TqInt i = 0; i < cVertex; ++i )
					P->pValue( i )[0] = CqVector4D( pPoints[ ( i * 4 ) ], pPoints[ ( i * 4 ) + 1 ], pPoints[ ( i * 4 ) + 2 ], pPoints[ ( i * 4 ) + 3 ] );
				break;
		}
		pSurface->AddPrimitiveVariable(P);
	}

	// Now process any user defined paramter variables.
	if ( aUserParams.size() > 0 )
	{
		std::vector<TqInt>::iterator iUserParam;
		for ( iUserParam = aUserParams.begin(); iUserParam != aUserParams.end(); ++iUserParam )
		{
			const Ri::Param& param = pList[*iUserParam];
			CqPrimvarToken tok(param.spec(), param.name());

			CqParameter* pNewParam = CqParameter::Create(tok);
			// Now go across all values and fill in the parameter variable.
			TqInt cValues = 1;
			switch ( tok.Class() )
			{
				case class_uniform:
					cValues = pSurface->cUniform();
					break;

				case class_varying:
					cValues = pSurface->cVarying();
					break;

				case class_vertex:
					cValues = pSurface->cVertex();
					break;

				case class_facevarying:
					cValues = pSurface->cFaceVarying();
					break;

				case class_facevertex:
					cValues = pSurface->cFaceVertex();
					break;

				default:
					break;
			}
			pNewParam->SetSize( cValues );

			const void* value = param.data();
			TqInt i;
			switch ( tok.type() )
			{
					case type_float:
					{
						CqParameterTyped<TqFloat, TqFloat>* pFloatParam = static_cast<CqParameterTyped<TqFloat, TqFloat>*>( pNewParam );
						const TqFloat* pValue = reinterpret_cast<const TqFloat*>(value);
						TqInt iArrayIndex, iValIndex;
						i = 0;
						for ( iValIndex = 0; iValIndex < cValues; ++iValIndex )
							for ( iArrayIndex = 0; iArrayIndex < tok.count(); ++iArrayIndex, ++i )
								pFloatParam->pValue( iValIndex ) [ iArrayIndex ] = pValue[ i ];
					}
					break;

					case type_integer:
					{
						CqParameterTyped<TqInt, TqFloat>* pIntParam = static_cast<CqParameterTyped<TqInt, TqFloat>*>( pNewParam );
						const TqInt* pValue = reinterpret_cast<const TqInt*>(value);
						TqInt iArrayIndex, iValIndex;
						i = 0;
						for ( iValIndex = 0; iValIndex < cValues; ++iValIndex )
							for ( iArrayIndex = 0; iArrayIndex < tok.count(); ++iArrayIndex, ++i )
								pIntParam->pValue( iValIndex ) [ iArrayIndex ] = pValue[ i ];
					}
					break;

					case type_point:
					case type_normal:
					case type_vector:
					{
						CqParameterTyped<CqVector3D, CqVector3D>* pVectorParam = static_cast<CqParameterTyped<CqVector3D, CqVector3D>*>( pNewParam );
						const TqFloat* pValue = reinterpret_cast<const TqFloat*>(value);
						TqInt iArrayIndex, iValIndex;
						i = 0;
						for ( iValIndex = 0; iValIndex < cValues; ++iValIndex )
							for ( iArrayIndex = 0; iArrayIndex < tok.count(); ++iArrayIndex, ++i )
								pVectorParam->pValue( iValIndex ) [ iArrayIndex ] = CqVector3D( pValue[ ( i * 3 ) ], pValue[ ( i * 3 ) + 1 ], pValue[ ( i * 3 ) + 2 ] );
					}
					break;

					case type_string:
					{
						CqParameterTyped<CqString, CqString>* pStringParam = static_cast<CqParameterTyped<CqString, CqString>*>( pNewParam );
						const char* const* pValue = reinterpret_cast<const char* const*>(value);
						TqInt iArrayIndex, iValIndex;
						i = 0;
						for ( iValIndex = 0; iValIndex < cValues; ++iValIndex )
							for ( iArrayIndex = 0; iArrayIndex < tok.count(); ++iArrayIndex, ++i )
								pStringParam->pValue( iValIndex ) [ iArrayIndex ] = CqString( pValue[ i ] );
					}
					break;

					case type_color:
					{
						CqParameterTyped<CqColor, CqColor>* pColorParam = static_cast<CqParameterTyped<CqColor, CqColor>*>( pNewParam );
						const TqFloat* pValue = reinterpret_cast<const TqFloat*>(value);
						TqInt iArrayIndex, iValIndex;
						i = 0;
						for ( iValIndex = 0; iValIndex < cValues; ++iValIndex )
							for ( iArrayIndex = 0; iArrayIndex < tok.count(); ++iArrayIndex, ++i )
								pColorParam->pValue( iValIndex ) [ iArrayIndex ] = CqColor( pValue[ ( i * 3 ) ], pValue[ ( i * 3 ) + 1 ], pValue[ ( i * 3 ) + 2 ] );
					}
					break;

					case type_hpoint:
					{
						CqParameterTyped<CqVector4D, CqVector3D>* pVectorParam = static_cast<CqParameterTyped<CqVector4D, CqVector3D>*>( pNewParam );
						const TqFloat* pValue = reinterpret_cast<const TqFloat*>(value);
						TqInt iArrayIndex, iValIndex;
						i = 0;
						for ( iValIndex = 0; iValIndex < cValues; ++iValIndex )
							for ( iArrayIndex = 0; iArrayIndex < tok.count(); ++iArrayIndex, ++i )
								pVectorParam->pValue( iValIndex ) [ iArrayIndex ] = CqVector4D( pValue[ ( i * 4 ) ], pValue[ ( i * 4 ) + 1 ], pValue[ ( i * 4 ) + 2 ], pValue[ ( i * 4 ) + 3 ] );
					}
					break;

					case type_matrix:
					{
						CqParameterTyped<CqMatrix, CqMatrix>* pMatrixParam = static_cast<CqParameterTyped<CqMatrix, CqMatrix>*>( pNewParam );
						const TqFloat* pValue = reinterpret_cast<const TqFloat*>(value);
						TqInt iArrayIndex, iValIndex;
						i = 0;
						for ( iValIndex = 0; iValIndex < cValues; ++iValIndex )
							for ( iArrayIndex = 0; iArrayIndex < tok.count(); ++iArrayIndex, ++i )
								pMatrixParam->pValue( iValIndex ) [ iArrayIndex ] = CqMatrix( pValue[ ( i * 16 ) ], pValue[ ( i * 16 ) + 1 ], pValue[ ( i * 16 ) + 2 ], pValue[ ( i * 16 ) + 3 ],
								        pValue[ ( i * 16 ) + 4 ], pValue[ ( i * 16 ) + 5 ], pValue[ ( i * 16 ) + 6 ], pValue[ ( i * 16 ) + 7 ],
								        pValue[ ( i * 16 ) + 8 ], pValue[ ( i * 16 ) + 9 ], pValue[ ( i * 16 ) + 10 ], pValue[ ( i * 16 ) + 11 ],
								        pValue[ ( i * 16 ) + 12 ], pValue[ ( i * 16 ) + 13 ], pValue[ ( i * 16 ) + 14 ], pValue[ ( i * 16 ) + 15 ]
								                                                            );
					}
					break;

					default:
					{
						// left blank to avoid compiler warnings about unhandled types
						break;
					}
			}
			pSurface->AddPrimitiveVariable( pNewParam );
		}
	}

	return ( pSurface->P() != NULL );
}


//----------------------------------------------------------------------
// CreateGPrin
// Create and register a GPrim according to the current attributes/transform
//
RtVoid	CreateGPrim( const boost::shared_ptr<CqSurface>& pSurface )
{
	// If in a motion block, confirm that the current deformation surface can accept the passed one as a keyframe.
	if( QGetRenderContext() ->pconCurrent() ->fMotionBlock() )
	{
		CqMotionModeBlock* pMMB = static_cast<CqMotionModeBlock*>(QGetRenderContext() ->pconCurrent().get());

		CqDeformingSurface* pMS = pMMB->GetDeformingSurface().get();
		// If this is the first frame, then generate the appropriate CqDeformingSurface and fill in the first frame.
		// Then cache the pointer on the motion block.
		if( pMS == NULL )
		{
			boost::shared_ptr<CqDeformingSurface> pNewMS( new CqDeformingSurface( pSurface ) );
			pNewMS->AddTimeSlot( QGetRenderContext()->Time(), pSurface );
			pMMB->SetDeformingSurface( pNewMS );
		}
		else
		{
			pMS->AddTimeSlot( QGetRenderContext()->Time(), pSurface );
		}
		QGetRenderContext() ->AdvanceTime();
	}
	else
	{
		QGetRenderContext()->StorePrimitive( pSurface );
		STATS_INC( GPR_created );

		// Add to the raytracer database also
		if(QGetRenderContext()->pRaytracer())
			QGetRenderContext()->pRaytracer()->AddPrimitive(pSurface);
	}
}


//==============================================================================
/// Api services for the core renderer.
class CoreRendererServices : public Ri::RendererServices
{
	public:
		CoreRendererServices()
			: m_renderContext(new CqRenderer()),
			m_api(),
			m_parser(),
			m_filterChain(),
			m_errorHandler()
		{
			m_api.reset(new RiCxxCore(*this));
			// Add renderer utility filter.  We do this here rather than in
			// addFilter() because this is a special filter which should only
			// be added once.
			Ri::Filter* utilFilter = createRenderUtilFilter(TestCondition);
			utilFilter->setNextFilter(*m_api);
			utilFilter->setRendererServices(*this);
			m_filterChain.push_back(boost::shared_ptr<Ri::Renderer>(utilFilter));
			// Add RI validation
			addFilter("validate");
		}

		CqRenderer& renderContext()
		{
			return *m_renderContext;
		}

		//--------------------------------------------------
		// from Ri::RenderServices
		virtual Ri::ErrorHandler& errorHandler()
		{
			return m_errorHandler;
		}

		virtual RtFilterFunc getFilterFunc(RtConstToken name) const
		{
			return getFilterFuncByName(name);
		}
		virtual RtConstBasis* getBasis(RtConstToken name) const
		{
			return getBasisByName(name);
		}
		virtual RtErrorFunc getErrorFunc(RtConstToken name) const
		{
			return getErrorFuncByName(name);
		}
		virtual RtProcSubdivFunc getProcSubdivFunc(RtConstToken name) const
		{
			return getProcSubdivFuncByName(name);
		}

		virtual Ri::TypeSpec getDeclaration(RtConstToken token,
										const char** nameBegin = 0,
										const char** nameEnd = 0) const
		{
			return m_renderContext->tokenDict().lookup(token, nameBegin,
													   nameEnd);
        }

        virtual Ri::Renderer& firstFilter()
        {
            if(!m_filterChain.empty())
                return *m_filterChain.back();
            return *m_api;
        }

        virtual void addFilter(const char* name,
                               const Ri::ParamList& filterParams = Ri::ParamList())
        {
            boost::shared_ptr<Ri::Filter> filter;
            if(!strcmp(name, "echorib"))
            {
                if(!m_echoRibWriter)
                {
                    RibWriterOptions opts;
                    opts.handleProcedurals = false;
                    m_echoRibWriter.reset(createRibWriter(std::cout, opts));
                    // We need to kill off all ReadArchive calls, otherwise we
                    // get them _twice_ in the output stream, since the
                    // renderer reads the archive into the start of the filter
                    // chain.
                    m_echoRibWriter->addFilter("ignorearchives");
                    registerStdFuncs(*m_echoRibWriter);
                }
                filter.reset(createTeeFilter(m_echoRibWriter->firstFilter()));
            }
            else
                filter.reset(createFilter(name, filterParams));
            if(filter)
            {
                filter->setNextFilter(firstFilter());
                filter->setRendererServices(*this);
                m_filterChain.push_back(filter);
            }
            else
            {
                AQSIS_THROW_XQERROR(XqValidation, EqE_BadToken,
                        "filter \"" << name << "\" not found");
            }
        }

        virtual void addFilter(Ri::Filter& filter)
        {
            filter.setNextFilter(firstFilter());
            filter.setRendererServices(*this);
            m_filterChain.push_back(boost::shared_ptr<Ri::Renderer>(&filter,
                                                                    nullDeleter));
        }

        virtual void parseRib(std::istream& ribStream, const char* name,
                              Ri::Renderer& context)
        {
            if(!m_parser)
                m_parser.reset(RibParser::create(*this));
            m_parser->parseStream(ribStream, name, context);
        }

    private:
        /// Core render context
        boost::shared_ptr<CqRenderer> m_renderContext;
        /// Core renderer API
        boost::shared_ptr<RiCxxCore> m_api;
        /// RIB writer for the echo API filter
        boost::shared_ptr<RibWriterServices> m_echoRibWriter;
        /// Parser for ReadArchive.  May be NULL (created on demand).
        boost::shared_ptr<RibParser> m_parser;
        /// Chain of filters
        std::vector<boost::shared_ptr<Ri::Renderer> > m_filterChain;
        /// Error handler.
        AqsisLogErrorHandler m_errorHandler;
};

} // namespace Aqsis;

//==============================================================================
// Remaining Ri* function definitions.
//==============================================================================

using namespace Aqsis;

//----------------------------------------------------------------------
// Context handling
namespace {
struct CoreContext
{
	boost::shared_ptr<Ri::RendererServices> apiServices;
	boost::shared_ptr<std::ofstream> writerOutput;
	CqRenderer* renderContext;
	void* riToRiCxxData;

	CoreContext(RtToken name)
		: apiServices(),
		writerOutput(),
		renderContext(0),
		riToRiCxxData(0)
	{
		if(!name || *name == '\0')
		{
			// By default, create a core renderer object
			CoreRendererServices* serv = new CoreRendererServices();
			apiServices.reset(serv);
			renderContext = &serv->renderContext();
		}
		else
		{
			// Note - would need std::ios::binary if we wanted to write binary
			// RIB.
			writerOutput.reset(new std::ofstream(name));
			RibWriterServices* writer =
				createRibWriter(*writerOutput, RibWriterOptions());
			apiServices.reset(writer);
			registerStdFuncs(*writer);
		}
	}
};
}

namespace Aqsis {
void riToRiCxxOptionPreBegin(RtToken name, RtInt count, RtToken* tokens,
							 RtPointer* values)
{ }
}

/// RI context
static CoreContext* g_context = 0;
typedef std::vector<CoreContext*> ContextList;
static ContextList g_validContexts;

RtVoid RiBegin(RtToken name)
{
	// Make a context
	g_validContexts.push_back(new CoreContext(name));
	g_context = g_validContexts.back();
	g_context->riToRiCxxData = riToRiCxxBegin(*g_context->apiServices);
	QSetRenderContext(g_context->renderContext);

	if(QGetRenderContext())
	{
		QGetRenderContext() ->Initialise();
		QGetRenderContext() ->BeginMainModeBlock();
		QGetRenderContext() ->ptransSetTime( CqMatrix() );
		QGetRenderContext() ->SetCameraTransform( QGetRenderContext() ->ptransCurrent() );

		SetDefaultRiOptions();

		// Setup a default surface shader
		boost::shared_ptr<IqShader> pDefaultSurfaceShader =
			QGetRenderContext()->getDefaultSurfaceShader();
		QGetRenderContext() ->pattrWriteCurrent() ->SetpshadSurface( pDefaultSurfaceShader, QGetRenderContext() ->Time() );

		// Setup the initial transformation.
		//	QGetRenderContext()->ptransWriteCurrent() ->SetHandedness( false );
		QGetRenderContext() ->pattrWriteCurrent() ->GetIntegerAttributeWrite( "System", "Orientation" ) [ 0 ] = 0;
	}
}

RtVoid RiEnd()
{
	if(QGetRenderContext())
		QGetRenderContext() ->EndMainModeBlock();

	// Delete the renderer
	QSetRenderContext( 0 );
	riToRiCxxEnd();
	g_validContexts.erase(std::find(g_validContexts.begin(),
									g_validContexts.end(), g_context));
	delete g_context;
}

RtContextHandle RiGetContext()
{
	return g_context;
}

RtVoid RiContext(RtContextHandle handle)
{
	CoreContext* newContext = static_cast<CoreContext*>(handle);
	ContextList::iterator loc = std::find(g_validContexts.begin(),
										  g_validContexts.end(), newContext);
	if(loc == g_validContexts.end())
	{
		g_context->apiServices->errorHandler().error(EqE_BadHandle,
											"bad handle for RiContext");
		return;
	}
	g_context = newContext;
	riToRiCxxContext(g_context->riToRiCxxData);
	QSetRenderContext(g_context->renderContext);
}

namespace Aqsis {
Ri::RendererServices* cxxRenderContext()
{
	return g_context->apiServices.get();
}
}

//----------------------------------------------------------------------
// Standard Error Handlers

RtInt	RiLastError = RIE_NOERROR;

/** RiErrorIgnore
 * Standard error handler where the error is ignored and no diagnostic
 * message is generated
 * \param code indicates the type of error
 * \param severity indicates how serious the error is
 * \param message is a character string containing an error message
 */
RtVoid RiErrorIgnore(RtInt code, RtInt severity, RtString message)
{
	RiLastError = code;
}

/** RiErrorPrint
 * Standard error handler where a diagnostic message is generated for the error. The
 * rendering system will attempt to ignore this erroneous information and continue
 * rendering
 * \param code indicates the type of error
 * \param severity indicates how serious the error is
 * \param message is a character string containing an error message
 */
RtVoid RiErrorPrint(RtInt code, RtInt severity, RtString message)
{
	// Store the code in RiLastError
	RiErrorIgnore(code, severity, message);
	// Print the error message
	Aqsis::log() << error << "(" << code << ", " << severity << ") " << message << "\n";
}

/** RiErrorPrint
 * Standard error handler where the first error will cause a diagnostic message to be
 * generated and the rendering system will immediately terminate
 * \param code indicates the type of error
 * \param severity indicates how serious the error is
 * \param message is a character string containing an error message
 */
RtVoid RiErrorAbort(RtInt code, RtInt severity, RtString message)
{
	// Store the code in RiLastError &  Print the error message
	RiErrorPrint(code, severity, message);
	// Abort the engine
	std::exit(code);
}


//----------------------------------------------------------------------
// Transform a list of points from one coordinate system to another.
//
RtPoint* RiTransformPoints(RtToken fromspace, RtToken tospace, RtInt npoints, RtPoint points[])
{
	CqMatrix matCToW;
	if(QGetRenderContext() ->matSpaceToSpace( fromspace,
	                   tospace, NULL, NULL, QGetRenderContextI()->Time(), matCToW ))
	{
		if (matCToW.fIdentity() != true)
		{
			for(TqInt i =0; i< npoints; i++)
			{
				CqVector3D tmp(points[i]);
				tmp = matCToW * tmp;
				points[i][0] = tmp.x();
				points[i][1] = tmp.y();
				points[i][2] = tmp.z();
			}
		}

		return ( points );
	}
	return 0;
}


//----------------------------------------------------------------------
/** Get the basis matrix given a standard basis name.
 * \param b Storage for basis matrix.
 * \param strName Name of basis.
 * \return Boolean indicating the basis is valid.
 */

RtBoolean	BasisFromName( RtBasis * b, const char * strName )
{
	RtBasis * pVals = 0;
	if ( !strcmp( strName, "bezier" ) )
		pVals = &RiBezierBasis;
	else if ( !strcmp( strName, "bspline" ) )
		pVals = &RiBSplineBasis;
	else if ( !strcmp( strName, "catmull-rom" ) )
		pVals = &RiCatmullRomBasis;
	else if ( !strcmp( strName, "hermite" ) )
		pVals = &RiHermiteBasis;
	else if ( !strcmp( strName, "power" ) )
		pVals = &RiPowerBasis;

	if ( pVals )
	{
		TqInt i, j;
		for ( i = 0; i < 4; ++i )
			for ( j = 0; j < 4; ++j )
				( *b ) [ i ][ j ] = ( *pVals ) [ i ][ j ];
		return ( true );
	}
	return false;
}


//----------------------------------------------------------------------
/** Set the function used to report progress.
 *	\param	handler	Pointer to the new function to use.
 */
RtVoid RiProgressHandler(RtProgressFunc handler)
{
	QGetRenderContext()->SetpProgressHandler( handler );
}


//----------------------------------------------------------------------
// Deprecated interface features.

RtVoid RiDeformation(RtToken name, PARAMETERLIST)
{
	Aqsis::log() << warning << "RiDeformation not supported" << std::endl;
}

RtVoid RiDeformationV(RtToken name, RtInt n, RtToken tokens[], RtPointer parms[])
{
	Aqsis::log() << warning << "RiDeformationV not supported" << std::endl;
}

RtVoid RiMakeBump(RtString imagefile, RtString bumpfile, RtToken swrap, RtToken twrap, RtFilterFunc filterfunc, RtFloat swidth, RtFloat twidth, PARAMETERLIST)
{
	Aqsis::log() << warning << "RiMakeBump not supported" << std::endl;
}

