// Aqsis
// Copyright (C) 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 Implements the classes for handling micropolygrids and micropolygons.
		\author Paul C. Gregory (pgregory@aqsis.org)
*/

#include	"aqsis.h"
#include	"stats.h"
#include	"imagebuffer.h"
#include	"micropolygon.h"
#include	"renderer.h"
#include	"surface.h"
#include	"lights.h"
#include	"shaders.h"
#include	"trimcurve.h"
#include	"spline.h"

#include	"mpdump.h"

#include	"multitimer.h"

namespace Aqsis {


CqObjectPool<CqMicroPolygon> CqMicroPolygon::m_thePool;
CqObjectPool<CqMovingMicroPolygonKey>	CqMovingMicroPolygonKey::m_thePool;


void CqMicroPolyGridBase::CacheGridInfo(const boost::shared_ptr<const CqSurface>& surface)
{
	// ShadingRate may be modified by RiGeometricApproximation "focusfactor" or
	// "motionfactor", so we need to get the appropriate adjusted rate rather
	// than grabbing it directly from the grid attribute set.
	m_CurrentGridInfo.m_ShadingRate = surface->AdjustedShadingRate();
}

//---------------------------------------------------------------------
/** Default constructor
 */

CqMicroPolyGrid::CqMicroPolyGrid() : CqMicroPolyGridBase(),
		m_bShadingNormals( false ),
		m_bGeometricNormals( false ), 
		m_pShaderExecEnv(new CqShaderExecEnv(QGetRenderContextI()))
{
	STATS_INC( GRD_allocated );
	STATS_INC( GRD_current );
	STATS_INC( GRD_allocated );
	TqInt cGRD = STATS_GETI( GRD_current );
	TqInt cPeak = STATS_GETI( GRD_peak );
	STATS_SETI( GRD_peak, cGRD > cPeak ? cGRD : cPeak );
}


//---------------------------------------------------------------------
/** Destructor
 */

CqMicroPolyGrid::~CqMicroPolyGrid()
{
	assert( RefCount() <= 0 );

	STATS_INC( GRD_deallocated );
	STATS_DEC( GRD_current );

	// Delete any cloned shader output variables.
	std::vector<IqShaderData*>::iterator outputVar;
	for( outputVar = m_apShaderOutputVariables.begin(); outputVar != m_apShaderOutputVariables.end(); outputVar++ )
		if( (*outputVar) )
			delete( (*outputVar) );
	m_apShaderOutputVariables.clear();
}


//---------------------------------------------------------------------
/** Initialise the grid ready for processing.
 * \param cu Integer grid resolution.
 * \param cv Integer grid resolution.
 * \param pSurface CqSurface pointer to associated GPrim.
 */

void CqMicroPolyGrid::Initialise( TqInt cu, TqInt cv, const boost::shared_ptr<CqSurface>& pSurface )
{
	// Initialise the shader execution environment
	TqInt lUses = -1;
	if ( pSurface )
	{
		lUses = pSurface->Uses();
		m_pSurface = pSurface;

		m_pCSGNode = pSurface->pCSGNode();
	}
	lUses |= QGetRenderContext()->pDDmanager()->Uses();

	/// \note This should delete through the interface that created it.

	m_pShaderExecEnv->Initialise( cu, cv, numMicroPolygons(cu, cv), numShadingPoints(cu, cv), hasValidDerivatives(), pSurface->pAttributes(), pSurface->pTransform(), pSurface->pAttributes()->pshadSurface(QGetRenderContext()->Time()).get(), lUses );

	boost::shared_ptr<IqShader> pshadSurface = pSurface ->pAttributes() ->pshadSurface(QGetRenderContext()->Time());
	boost::shared_ptr<IqShader> pshadDisplacement = pSurface ->pAttributes() ->pshadDisplacement(QGetRenderContext()->Time());
	boost::shared_ptr<IqShader> pshadAtmosphere = pSurface ->pAttributes() ->pshadAtmosphere(QGetRenderContext()->Time());

	if ( pshadSurface )
		pshadSurface->Initialise( cu, cv, numShadingPoints(cu, cv), m_pShaderExecEnv );
	if ( pshadDisplacement )
		pshadDisplacement->Initialise( cu, cv, numShadingPoints(cu, cv), m_pShaderExecEnv );
	if ( pshadAtmosphere )
		pshadAtmosphere->Initialise( cu, cv, numShadingPoints(cu, cv), m_pShaderExecEnv );

	// Initialise the local/public culled variable.
	m_CulledPolys.SetSize( numShadingPoints(cu, cv) );
	m_CulledPolys.SetAll( false );

	TqInt size = numMicroPolygons(cu, cv);
	CacheGridInfo(pSurface);

	STATS_INC( GRD_size_4 + clamp<TqInt>(CqStats::stats_log2(size) - 2, 0, 7) );
}

//---------------------------------------------------------------------
/** Build the normals list for the micropolygons in the grid.
 */

void CqMicroPolyGrid::CalcNormals()
{
	if ( NULL == pVar(EnvVars_P) || NULL == pVar(EnvVars_N) )
		return ;

	// We flip the normals if the 'current orientation' differs from the
	// 'coordinate system orientation' - see RiSpec 'Orientation and Sides'
	//
	// The RiSpec is a little tricky to interpret on this point, so here's a
	// summary of their intent using a sphere as an example:
	//
	// No matter which coordinate transformation you perform, the normals of a
	// sphere should always end up pointing outwards from the centre by
	// default.  To make them point inwards, use RiReverseOrientation().
	//
	// The mess happens since you want this to be true even when using
	// transformations which change the handedness, such as RiScale(1,1,-1).
	// By convention, the normal is
	//
	//   N = dPdu x dPdv
	//
	// This is fine in the default coordinate system.  However, the direction
	// of the cross product must be reversed if the formula is to give the
	// correct normal after RiScale(1,1,-1) or similar transformations.
	bool CSO = this->pSurface()->pTransform()->GetHandedness(this->pSurface()->pTransform()->Time(0));
	bool O = pAttributes() ->GetIntegerAttribute( "System", "Orientation" ) [ 0 ] != 0;
	bool flipNormals = O ^ CSO;

	const CqVector3D* vecMP[ 4 ];
	CqVector3D	vecN, vecTemp;
	CqVector3D	vecFailsafeN;

	const CqVector3D* pP;
	pVar(EnvVars_P) ->GetPointPtr( pP );
	IqShaderData* pNg = pVar(EnvVars_Ng);

	// Calculate each normal from the top left, top right and bottom left points.
	register TqInt ur = uGridRes();
	register TqInt vr = vGridRes();

	// Create a failsafe normal from the corners of the grid, in case we encounter degenerate MP's
	vecFailsafeN = ( pP[ur] - pP[0] ) % ( pP[(vr*(ur+1))+ur] - pP[0] );
	vecFailsafeN.Unit();

	TqInt igrid = 0;
	TqInt iv;
	for ( iv = 0; iv < vr; iv++ )
	{
		TqInt iu;
		for ( iu = 0; iu < ur; iu++ )
		{
			vecMP[ 0 ] = &pP[ igrid ];
			vecMP[ 1 ] = &pP[ igrid + 1 ];
			vecMP[ 2 ] = &pP[ igrid + ur + 2 ];
			vecMP[ 3 ] = &pP[ igrid + ur + 1];
			TqInt a=0, b=1, c=2;
			CqVector3D vecBA = ( *vecMP[ b ] ) - ( *vecMP[ a ] );
			CqVector3D vecCA = ( *vecMP[ c ] ) - ( *vecMP[ a ] );
			TqFloat bma = vecBA.Magnitude();
			TqFloat cma = vecCA.Magnitude();
			if( bma < FLT_EPSILON )
			{
				b = 3;
				vecBA = ( *vecMP[ b ] ) - ( *vecMP[ a ] );
				bma = vecBA.Magnitude();
			}


			if( ( bma > FLT_EPSILON ) &&
			        ( cma > FLT_EPSILON ) &&
			        ( vecBA != vecCA ) )
			{
				vecN = vecBA % vecCA;	// Cross product is normal.*/
				vecN.Unit();
				if(flipNormals)
					vecN = -vecN;
			}
			else
			{
				//assert(false);
				vecN = vecFailsafeN;
			}

			pNg->SetNormal( vecN, igrid );
			igrid++;
			// If we are at the last row, last row normal to the same.
			if ( iv == vr - 1 )
			{
				CqVector3D vecNN( vecN );
				if ( vr > 2 )
				{
					CqVector3D vecNm1, vecNm2;
					pNg->GetNormal( vecNm1, ( ( vr - 1 ) * ( ur + 1 ) ) + iu );
					pNg->GetNormal( vecNm2, ( ( vr - 2 ) * ( ur + 1 ) ) + iu );
					vecNN = ( vecNm1 - vecNm2 ) + vecN;
				}
				pNg->SetNormal( vecNN, ( vr * ( ur + 1 ) ) + iu );
			}
		}
		// Set the last one on the row to the same.
		CqVector3D vecNN( vecN );
		if ( igrid > 2 )
		{
			CqVector3D vecNm1, vecNm2;
			pNg->GetNormal( vecNm1, igrid - 1 );
			pNg->GetNormal( vecNm2, igrid - 2 );
			vecNN = ( vecNm1 - vecNm2 ) + vecN;
		}
		pNg->SetNormal( vecNN, igrid );
		igrid++;
	}
	// Set the very last corner value to the last normal calculated.
	CqVector3D vecNN( vecN );
	if ( vr > 2 && ur > 2 )
	{
		CqVector3D vecNm1, vecNm2;
		pNg->GetNormal( vecNm1, ( vr - 1 ) * ( ur - 1 ) - 1 );
		pNg->GetNormal( vecNm2, ( vr - 2 ) * ( ur - 2 ) - 1 );
		vecNN = ( vecNm1 - vecNm2 ) + vecN;
	}
	pNg->SetNormal( vecNN, ( vr + 1 ) * ( ur + 1 ) - 1 );
}

void CqMicroPolyGrid::CalcSurfaceDerivatives()
{
	/// \todo <b>Code review</b>: This function should probably belong in the shaderexecenv.
	// It could then be easily modified to use the new centered difference functions in the shaderexecenv
	bool bdpu, bdpv;
	TqInt lUses = pSurface() ->Uses();
	bdpu = ( USES( lUses, EnvVars_dPdu ) );
	bdpv = ( USES( lUses, EnvVars_dPdv ) );
	IqShaderData * pSDP = pVar(EnvVars_P);
	static CqVector3D	Defvec( 0, 0, 0 );

	TqInt i;

	TqInt gsmin1 = m_pShaderExecEnv->shadingPointCount() - 1;
	for ( i = gsmin1; i >= 0; i-- )
	{
		if ( bdpu )
		{
			pVar(EnvVars_dPdu) ->SetVector( SO_DuType<CqVector3D>( pSDP, i, m_pShaderExecEnv.get(), Defvec ), i );
		}
		if ( bdpv )
		{
			pVar(EnvVars_dPdv) ->SetVector( SO_DvType<CqVector3D>( pSDP, i, m_pShaderExecEnv.get(), Defvec ), i );
		}
	}
}


//---------------------------------------------------------------------
void CqMicroPolyGrid::ExpandGridBoundaries(TqFloat amount)
{
	// Anti grid-crack hack!  We expand the grid by pushing the outer verts
	// along the vectors connecting themselves to the next layer of verts in:
	//
	//        ^  ^  ^
	//        |  |  |
	//
	//  <--   x--x--x ...
	//        |  |  |
	//  <--   x--x--x ...
	//        |  |  .
	//  <--   x--x.
	//        .
	//
	CqVector3D* pP;
	pVar(EnvVars_P)->GetPointPtr(pP);

	const TqInt numVertsU = uGridRes() + 1;
	const TqInt totVerts = numVertsU*(vGridRes() + 1);

	// Before expanding each edge, we perform a quick check to guess whether it
	// is degenerate.  We avoid checking the vertices of a row against each
	// other directly, since this will result in the usual sensitivity to
	// floating point errors.  Instead, we check whether the length of the row
	// is less than sqrt(degeneracyRatio) times the length of the following
	// row.
	const TqFloat degeneracyRatio = 1e-8;

	if((pP[0] - pP[numVertsU-1]).Magnitude2()
			> degeneracyRatio*(pP[numVertsU] - pP[2*numVertsU-1]).Magnitude2())
	{
		// Expand first u-row in -v direction.
		for(TqInt iu = 0; iu < numVertsU; ++iu)
			pP[iu] = (1+amount)*pP[iu] - amount*pP[iu+numVertsU];
	}
	if((pP[totVerts-numVertsU] - pP[totVerts-1]).Magnitude2()
			> degeneracyRatio*(pP[totVerts-2*numVertsU] - pP[totVerts-numVertsU-1]).Magnitude2())
	{
		// Expand last u-row in +v direction.
		for(TqInt iu = totVerts-numVertsU; iu < totVerts; ++iu)
			pP[iu] = (1+amount)*pP[iu] - amount*pP[iu-numVertsU];
	}
	if((pP[0] - pP[totVerts-numVertsU]).Magnitude2()
			> degeneracyRatio*(pP[1] - pP[totVerts-numVertsU+1]).Magnitude2())
	{
		// Expand first v-column in -u direction.
		for(TqInt iv = 0; iv < totVerts; iv += numVertsU)
			pP[iv] = (1+amount)*pP[iv] - amount*pP[iv+1];
	}
	if((pP[numVertsU-1] - pP[totVerts-1]).Magnitude2()
			> degeneracyRatio*(pP[numVertsU-2] - pP[totVerts-2]).Magnitude2())
	{
		// Expand last v-column in +u direction.
		for(TqInt iv = numVertsU-1; iv < totVerts; iv += numVertsU)
			pP[iv] = (1+amount)*pP[iv] - amount*pP[iv-1];
	}
}


//---------------------------------------------------------------------
/** Shade the grid using the surface parameters of the surface passed and store the color values for each micropolygon.
 */

void CqMicroPolyGrid::Shade( bool canCullGrid )
{
	// Sanity checks
	if ( NULL == pVar(EnvVars_P) || NULL == pVar(EnvVars_I) )
		return ;

	TqInt lUses = pSurface() ->Uses();
	TqInt gs = m_pShaderExecEnv->shadingPointCount();
	TqInt gsmin1 = gs - 1;

	// Expand grids to prevent grid cracking if enabled
	const TqFloat* gridExpand = pAttributes()->GetFloatAttribute("aqsis", "expandgrids");
	if(gridExpand && *gridExpand > 0)
		ExpandGridBoundaries(*gridExpand);

	const CqVector3D* pP;
	pVar(EnvVars_P) ->GetPointPtr( pP );
	const CqColor* pOs = NULL;
	if ( USES( lUses, EnvVars_Os ) )
		pVar(EnvVars_Os) ->GetColorPtr( pOs );
	const CqColor* pCs = NULL;
	if ( USES( lUses, EnvVars_Cs ) )
		pVar(EnvVars_Cs) ->GetColorPtr( pCs );
	IqShaderData* pI = pVar(EnvVars_I);
	const CqVector3D* pN = NULL;
	if ( USES( lUses, EnvVars_N ) )
		pVar(EnvVars_N) ->GetNormalPtr( pN );

	// Calculate geometric normals if not specified by the surface.
	if ( !bGeometricNormals() && USES( lUses, EnvVars_Ng ) )
		CalcNormals();

	// If shading normals are not explicitly specified, they default to the geometric normal.
	if ( !bShadingNormals() && USES( lUses, EnvVars_N ) && NULL != pVar(EnvVars_Ng) && NULL != pVar(EnvVars_N) )
		pVar(EnvVars_N) ->SetValueFromVariable( pVar(EnvVars_Ng) );

	// Set eye position - always at the origin in the shading coord system.
	if ( USES( lUses, EnvVars_E ) )
		pVar(EnvVars_E)->SetVector(CqVector3D(0, 0, 0));

	// Set du and dv if necessary.  This code assumes that du and dv are
	// constant across every grid. (Looks to be a good assumption at svn r2117.)
	/// \todo: Should this be a method of the shaderexecenv?
	if(USES(lUses, EnvVars_du))
		setDu();
	if(USES(lUses, EnvVars_dv))
		setDv();

	// Set I, the incident ray direction; this is just equal to P in shading
	// (camera) coords for a projective camera transformation, or (0,0,1) for
	// orthographic.
	switch(QGetRenderContext()->GetIntegerOption("System", "Projection")[0])
	{
		case ProjectionOrthographic:
			pI->SetVector(CqVector3D(0,0,1));
			break;
		case ProjectionPerspective:
		default:
			pI->SetValueFromVariable(pVar(EnvVars_P));
			break;
	}

	// Calculate surface derivatives if necessary.
	if ( USES( lUses, EnvVars_dPdu ) || USES( lUses, EnvVars_dPdv ) )
		CalcSurfaceDerivatives();

	// Initialize surface color Ci to black
	if ( USES( lUses, EnvVars_Ci ) )
		pVar(EnvVars_Ci) ->SetColor( gColBlack );
	// Initialize surface opacity Oi to opaque
	if ( USES( lUses, EnvVars_Oi ) )
		pVar(EnvVars_Oi) ->SetColor( gColWhite );

	if(USES(lUses, EnvVars_Os))
	{
		if(QGetRenderContext() ->poptCurrent()->GetIntegerOption( "System", "DisplayMode" ) [ 0 ] & ModeZ)
		{
			// Try to cull any transparent MPs
			TqInt cCulled = 0;
			TIME_SCOPE("Occlusion Culling")
			for (TqInt i = gsmin1; i >= 0; i-- )
			{
				if ( pOs[ i ] != gColWhite )
				{
					cCulled ++;
					m_CulledPolys.SetValue( i, true );
				}
				else
					break;
			}

			if ( canCullGrid && cCulled == gs )
			{
				m_fCulled = true;
				STATS_INC( GRD_culled );
				DeleteVariables( true );
				return ;
			}
		}
		if(QGetRenderContext() ->poptCurrent()->GetIntegerOption( "System", "DisplayMode" ) [ 0 ] & ModeRGB)
		{
			// Try to cull any true transparent MPs
			TqInt cCulled = 0;
			TIME_SCOPE("Occlusion culling")
			for (TqInt i = gsmin1; i >= 0; i-- )
			{
				if ( pOs[ i ] == gColBlack )
				{
					cCulled ++;
					m_CulledPolys.SetValue( i, true );
				}
				else
					break;
			}

			if ( canCullGrid && cCulled == gs )
			{
				m_fCulled = true;
				STATS_INC( GRD_culled );
				DeleteVariables( true );
				return ;
			}
		}
	}

	boost::shared_ptr<IqShader> pshadDisplacement = pSurface()->pAttributes()->pshadDisplacement(QGetRenderContext()->Time());
	if ( pshadDisplacement )
	{
		//theStats.DisplacementTimer().Start();
		TIME_SCOPE("Displacement")
		pshadDisplacement->Evaluate( m_pShaderExecEnv );
		//theStats.DisplacementTimer().Stop();

		// Re-calculate geometric normals and surface derivatives after displacement.
		// \note: This is a bit overkill, might be a better way of doing it.
		if ( USES( lUses, EnvVars_Ng ) )
			CalcNormals();
		if ( USES( lUses, EnvVars_dPdu ) || USES( lUses, EnvVars_dPdv ) )
			CalcSurfaceDerivatives();
	}

	// Now try and cull any hidden MPs if Sides==1
	if ( ( pAttributes() ->GetIntegerAttribute( "System", "Sides" ) [ 0 ] == 1 ) && !m_pCSGNode )
	{
		TqInt cCulled = 0;
		const CqVector3D* pNg = NULL;
		pVar(EnvVars_Ng) ->GetNormalPtr( pNg );

		TIME_SCOPE("Backface culling")
		// When backface culling, we must use the geometric normal (Ng) as
		// this is the normal that properly represents the actual micropolygon
		// geometry. However, if the primitive specifies custom normals as
		// primitive variables, the direction of those should be honored, in case
		// the user has intentionally switched the surface direction.
		// Therefore, we compare the direction of Ng with that of N and flip Ng 
		// if they don't match.
		for (TqInt i = gsmin1; i >= 0; i-- )
		{
			TqFloat s = 1.0f;
			if(NULL != pN)
				s = ( ( pN[i] * pNg[i] ) < 0.0f ) ? -1.0f : 1.0f;
			// Calulate the direction the MPG is facing.
			if ( ( ( s * pNg[ i ] ) * pP[ i ] ) >= 0 )
			{
				cCulled++;
				STATS_INC( MPG_culled );
				m_CulledPolys.SetValue( i, true );
			}
		}

		// If the whole grid is culled don't bother going any further.
		if ( canCullGrid && cCulled == gs )
		{
			m_fCulled = true;
			STATS_INC( GRD_culled );
			DeleteVariables( true );
			return ;
		}
	}

	// Now shade the grid.
	boost::shared_ptr<IqShader> pshadSurface = pSurface() ->pAttributes() ->pshadSurface(QGetRenderContext()->Time());
	if ( pshadSurface )
	{
		TIME_SCOPE("Surface shading")
		m_pShaderExecEnv->SetCurrentSurface(pSurface());
		pshadSurface->Evaluate( m_pShaderExecEnv );
	}

	// Perform atmosphere shading
	boost::shared_ptr<IqShader> pshadAtmosphere = pSurface()->pAttributes()->pshadAtmosphere(QGetRenderContext()->Time());
	if ( pshadAtmosphere )
	{
		TIME_SCOPE("Atmosphere shading")
		pshadAtmosphere->Evaluate( m_pShaderExecEnv );
	}

	// Now try to cull any true transparent MPs (assigned by the shader code
	if ( USES( lUses, EnvVars_Os ) && QGetRenderContext() ->poptCurrent()->GetIntegerOption( "System", "DisplayMode" ) [ 0 ] & ModeRGB )
	{
		TqInt cCulled = 0;
		TIME_SCOPE("Occlusion culling")
		for (TqInt i = gsmin1; i >= 0; i-- )
		{
			if ( pOs[ i ] == gColBlack )
			{
				cCulled ++;
				m_CulledPolys.SetValue( i, true );
			}
			else
				break;
		}

//		if ( cCulled == gs )
//		{
//			m_fCulled = true;
//			STATS_INC( GRD_culled );
//			DeleteVariables( true );
//			return ;
//		}
	}

	DeleteVariables( false );

	STATS_INC( GRD_shd_size_4 + clamp<TqInt>( CqStats::stats_log2(
					m_pShaderExecEnv->shadingPointCount() ) - 2, 0, 7 ) );
}

//---------------------------------------------------------------------
/** Transfer any shader variables marked as "otuput" as they may be needed by the display devices.
 */

void CqMicroPolyGrid::TransferOutputVariables()
{
	boost::shared_ptr<IqShader> pShader = this->pAttributes()->pshadSurface(QGetRenderContext()->Time());

	// Only bother transferring ones that have been used in a RiDisplay request.
	std::map<std::string, CqRenderer::SqOutputDataEntry>& outputVars = QGetRenderContext()->GetMapOfOutputDataEntries();
	std::map<std::string, CqRenderer::SqOutputDataEntry>::iterator outputVar;
	for( outputVar = outputVars.begin(); outputVar != outputVars.end(); outputVar++ )
	{
		IqShaderData* outputData = pShader->FindArgument( outputVar->first );
		if( NULL != outputData )
		{
			IqShaderData* newOutputData = outputData->Clone();
			m_apShaderOutputVariables.push_back( newOutputData );
		}
	}
}

//---------------------------------------------------------------------
/**
 * Delete unneeded variables so that we don't use up unnecessary memory
 */
void CqMicroPolyGrid::DeleteVariables( bool all )
{
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "Cs" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_Cs );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "Os" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_Os );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "du" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_du );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "dv" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_dv );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "L" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_L );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "Cl" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_Cl );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "Ol" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_Ol );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "dPdu" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_dPdu );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "dPdv" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_dPdv );

	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "s" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_s );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "t" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_t );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "I" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_I );

	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "Ps" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_Ps );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "E" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_E );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "ncomps" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_ncomps );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "time" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_time );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "alpha" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_alpha );

//	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "N" ) || all )
//		m_pShaderExecEnv->DeleteVariable( EnvVars_N );
	if (  /*!QGetRenderContext() ->pDDmanager()->fDisplayNeeds( "u" ) ||*/ all ) 		// \note: Needed by trim curves, need to work out how to check for their existence.
		m_pShaderExecEnv->DeleteVariable( EnvVars_u );
	if (  /*!QGetRenderContext() ->pDDmanager()->fDisplayNeeds( "v" ) ||*/ all ) 		// \note: Needed by trim curves, need to work out how to check for their existence.
		m_pShaderExecEnv->DeleteVariable( EnvVars_v );
	if ( all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_P );
//	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "Ng" ) || all )			// \note: Needed by backface culling.
//		m_pShaderExecEnv->DeleteVariable( EnvVars_Ng );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "Ci" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_Ci );
	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "Oi" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_Oi );

	if ( !QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "Ns" ) || all )
		m_pShaderExecEnv->DeleteVariable( EnvVars_Ns );
}



//---------------------------------------------------------------------
/** Split the shaded grid into microploygons, and insert them into the relevant buckets in the image buffer.
 * \param pImage Pointer to image being rendered into.
 * \param xmin Integer minimum extend of the image part being rendered, takes into account buckets and clipping.
 * \param xmax Integer maximum extend of the image part being rendered, takes into account buckets and clipping.
 * \param ymin Integer minimum extend of the image part being rendered, takes into account buckets and clipping.
 * \param ymax Integer maximum extend of the image part being rendered, takes into account buckets and clipping.
 */

void CqMicroPolyGrid::Split( CqImageBuffer* pImage, long xmin, long xmax, long ymin, long ymax )
{
	if ( NULL == pVar(EnvVars_P) )
		return ;

	TqInt cu = uGridRes();
	TqInt cv = vGridRes();

	TIMER_START("Project points")
	CqMatrix matCameraToRaster;
	QGetRenderContext() ->matSpaceToSpace( "camera", "raster", NULL, NULL, QGetRenderContext()->Time(), matCameraToRaster );
	CqMatrix matCameraToObject0;
	QGetRenderContext() ->matSpaceToSpace( "camera", "object", NULL, pSurface() ->pTransform().get(), QGetRenderContext()->Time(), matCameraToObject0 );

	// Transform the whole grid to hybrid camera/raster space
	CqVector3D* pP;
	pVar(EnvVars_P) ->GetPointPtr( pP );

	// Get an array of P's for all time positions, the transformation keyframes take 
	// into account transformation of the camera, as well as transformation of the object.
	// The list of keyframes is the union of the two.
	TqInt iTime;
	IqTransformPtr objectTransform = pSurface()->pTransform();
	CqTransformPtr cameraTransform = QGetRenderContext()->GetCameraTransform();
	TqInt objectTimes = objectTransform->cTimes();
	TqInt cameraTimes = cameraTransform->cTimes();
	std::map<TqFloat, TqInt> keyframeTimes;
	// Add all the object transformation times to the list of keyframe points.
	for(iTime = 0; iTime < objectTimes; iTime++)
		keyframeTimes[objectTransform->Time(iTime)] = iTime;
	if(cameraTimes > 1)
		for(iTime = 0; iTime < cameraTimes; iTime++)
			keyframeTimes[cameraTransform->Time(iTime)] = iTime;

	TqInt tTime = keyframeTimes.size();
	std::vector<std::vector<CqVector3D> > aaPtimes;
	aaPtimes.resize( tTime );

	CqMatrix matObjectToCameraT;
	register TqInt i;
	TqInt gsmin1;
	gsmin1 = m_pShaderExecEnv->shadingPointCount() - 1;

	std::map<TqFloat, TqInt>::iterator keyFrame;
	for ( keyFrame = keyframeTimes.begin(); keyFrame!=keyframeTimes.end(); keyFrame++ )
	{
		QGetRenderContext() ->matSpaceToSpace( "object", "camera", NULL, pSurface() ->pTransform().get(), keyFrame->first, matObjectToCameraT );
		aaPtimes[ keyFrame->second ].resize( gsmin1 + 1 );

		for ( i = gsmin1; i >= 0; i-- )
		{
			CqVector3D Point( pP[ i ] );

			// Only do the complex transform if motion blurred.
			//Point = matObjectToCameraT * matCameraToObject0 * Point;
			Point = matCameraToObject0 * Point;
			Point = matObjectToCameraT * Point;

			// Make sure to retain camera space 'z' coordinate.
			TqFloat zdepth = Point.z();
			aaPtimes[ keyFrame->second ][ i ] = matCameraToRaster * Point;
			aaPtimes[ keyFrame->second ][ i ].z( zdepth );
		}
		SqTriangleSplitLine sl;
		CqVector3D v0, v1, v2;
		v0 = aaPtimes[ keyFrame->second ][ 0 ];
		v1 = aaPtimes[ keyFrame->second ][ cu ];
		v2 = aaPtimes[ keyFrame->second ][ cv * ( cu + 1 ) ];
		// Check for clockwise, swap if not.
		if( ( ( v1.x() - v0.x() ) * ( v2.y() - v0.y() ) - ( v1.y() - v0.y() ) * ( v2.x() - v0.x() ) ) >= 0 )
		{
			sl.m_TriangleSplitPoint1 = v1;
			sl.m_TriangleSplitPoint2 = v2;
		}
		else
		{
			sl.m_TriangleSplitPoint1 = v2;
			sl.m_TriangleSplitPoint2 = v1;
		}
		m_TriangleSplitLine.AddTimeSlot(keyFrame->first, sl );
	}

	for ( i = gsmin1; i >= 0; i-- )
	{
		aaPtimes[ 0 ].resize( gsmin1 + 1 );
		// Make sure to retain camera space 'z' coordinate.
		TqFloat zdepth = pP[ i ].z();
		aaPtimes[ 0 ][ i ] = matCameraToRaster * pP[ i ];
		aaPtimes[ 0 ][ i ].z( zdepth );
		pP[ i ] = aaPtimes[ 0 ][ i ];
	}

	SqTriangleSplitLine sl;
	CqVector3D v0, v1, v2;
	v0 = aaPtimes[ 0 ][ 0 ];
	v1 = aaPtimes[ 0 ][ cu ];
	v2 = aaPtimes[ 0 ][ cv * ( cu + 1 ) ];
	// Check for clockwise, swap if not.
	if( ( ( v1.x() - v0.x() ) * ( v2.y() - v0.y() ) - ( v1.y() - v0.y() ) * ( v2.x() - v0.x() ) ) >= 0 )
	{
		sl.m_TriangleSplitPoint1 = v1;
		sl.m_TriangleSplitPoint2 = v2;
	}
	else
	{
		sl.m_TriangleSplitPoint1 = v2;
		sl.m_TriangleSplitPoint2 = v1;
	}
	m_TriangleSplitLine.AddTimeSlot(keyframeTimes.begin()->first, sl );

	TIMER_STOP("Project points")

	TIMER_START("Bust grids")
	// Get the required trim curve sense, if specified, defaults to "inside".
	const CqString* pattrTrimSense = pAttributes() ->GetStringAttribute( "trimcurve", "sense" );
	CqString strTrimSense( "inside" );
	if ( pattrTrimSense != 0 )
		strTrimSense = pattrTrimSense[ 0 ];
	bool bOutside = strTrimSense == "outside";

	// Determine whether we need to bother with trimming or not.
	bool bCanBeTrimmed = pSurface() ->bCanBeTrimmed() && NULL != pVar(EnvVars_u) && NULL != pVar(EnvVars_v);

	ADDREF( this );

	TqInt iv;
//	bool tooSmall_ = false;
//	TqFloat smallArea = 1.0;
//	TqFloat bigArea = 0.0;
	for ( iv = 0; iv < cv; iv++ )
	{
		TqInt iu;
		for ( iu = 0; iu < cu; iu++ )
		{
			TqInt iIndex = ( iv * ( cu + 1 ) ) + iu;

			// If culled don't bother.
			if ( m_CulledPolys.Value( iIndex ) )
			{
				STATS_INC(MPG_culled);
				continue;
			}

			// If the MPG is trimmed then don't add it.
			bool fTrimmed = false;
			if ( bCanBeTrimmed )
			{
				TqFloat u1, v1, u2, v2, u3, v3, u4, v4;

				pVar(EnvVars_u) ->GetFloat( u1, iIndex );
				pVar(EnvVars_v) ->GetFloat( v1, iIndex );
				pVar(EnvVars_u) ->GetFloat( u2, iIndex + 1 );
				pVar(EnvVars_v) ->GetFloat( v2, iIndex + 1 );
				pVar(EnvVars_u) ->GetFloat( u3, iIndex + cu + 2 );
				pVar(EnvVars_v) ->GetFloat( v3, iIndex + cu + 2 );
				pVar(EnvVars_u) ->GetFloat( u4, iIndex + cu + 1 );
				pVar(EnvVars_v) ->GetFloat( v4, iIndex + cu + 1 );

				CqVector2D vecA(u1, v1);
				CqVector2D vecB(u2, v2);
				CqVector2D vecC(u3, v3);
				CqVector2D vecD(u4, v4);

				bool fTrimA = pSurface() ->bIsPointTrimmed( vecA );
				bool fTrimB = pSurface() ->bIsPointTrimmed( vecB );
				bool fTrimC = pSurface() ->bIsPointTrimmed( vecC );
				bool fTrimD = pSurface() ->bIsPointTrimmed( vecD );

				if ( bOutside )
				{
					fTrimA = !fTrimA;
					fTrimB = !fTrimB;
					fTrimC = !fTrimC;
					fTrimD = !fTrimD;
				}

				// If all points are trimmed, need to check if the MP spans the trim curve at all, if not, then
				// we can discard it altogether.
				if ( fTrimA && fTrimB && fTrimC && fTrimD )
				{
					if(!pSurface()->bIsLineIntersecting(vecA, vecB) &&
					        !pSurface()->bIsLineIntersecting(vecB, vecC) &&
					        !pSurface()->bIsLineIntersecting(vecC, vecD) &&
					        !pSurface()->bIsLineIntersecting(vecD, vecA) )
					{
						STATS_INC( MPG_trimmedout );
						continue;
					}
				}

				// If any points are trimmed mark the MPG as needing to be trim checked.
				//fTrimmed = fTrimA || fTrimB || fTrimC || fTrimD;
				if ( fTrimA || fTrimB || fTrimC || fTrimD )
					fTrimmed = true;
			}

			if ( tTime > 1 )
			{
				CqMicroPolygonMotion * pNew = new CqMicroPolygonMotion();
				pNew->SetGrid( this );
				pNew->SetIndex( iIndex );
				if ( fTrimmed )
					pNew->MarkTrimmed();
				std::map<TqFloat, TqInt>::iterator keyFrame;
				for ( keyFrame = keyframeTimes.begin(); keyFrame!=keyframeTimes.end(); keyFrame++ )
					pNew->AppendKey( aaPtimes[ keyFrame->second ][ iIndex ], aaPtimes[ keyFrame->second ][ iIndex + 1 ], aaPtimes[ keyFrame->second ][ iIndex + cu + 2 ], aaPtimes[ keyFrame->second ][ iIndex + cu + 1 ],  keyFrame->first);
				pImage->AddMPG( pNew );
			}
			else
			{
				CqMicroPolygon *pNew = new CqMicroPolygon();
				pNew->SetGrid( this );
				pNew->SetIndex( iIndex );
				if ( fTrimmed )
					pNew->MarkTrimmed();
				pNew->Initialise();
				pNew->CalculateTotalBound();
				pImage->AddMPG( pNew );
			}

			// Calculate MPG area
			TqFloat area = 0.0f;
			area += ( aaPtimes[ 0 ][ iIndex ].x() * aaPtimes[ 0 ][iIndex + 1 ].y() ) - ( aaPtimes[ 0 ][ iIndex ].y() * aaPtimes[ 0 ][ iIndex + 1 ].x() );
			area += ( aaPtimes[ 0 ][ iIndex + 1].x() * aaPtimes[ 0 ][iIndex + cu + 2 ].y() ) - ( aaPtimes[ 0 ][ iIndex + 1].y() * aaPtimes[ 0 ][ iIndex + cu + 2 ].x() );
			area += ( aaPtimes[ 0 ][ iIndex + cu + 2].x() * aaPtimes[ 0 ][iIndex + cu + 1 ].y() ) - ( aaPtimes[ 0 ][ iIndex + cu + 2 ].y() * aaPtimes[ 0 ][ iIndex + cu + 1 ].x() );
			area += ( aaPtimes[ 0 ][ iIndex + cu + 1].x() * aaPtimes[ 0 ][iIndex ].y() ) - ( aaPtimes[ 0 ][ iIndex + cu + 1 ].y() * aaPtimes[ 0 ][ iIndex ].x() );
			area *= 0.5f;
			area = fabs(area);

			STATS_SETF( MPG_average_area, STATS_GETF( MPG_average_area ) + area );
			if( area < STATS_GETF( MPG_min_area ) )
				STATS_SETF( MPG_min_area, area );
			if( area > STATS_GETF( MPG_max_area ) )
				STATS_SETF( MPG_max_area, area );

		//	smallArea = std::min(smallArea, area);
		//	bigArea = std::max(bigArea, area);
		//	if(area < 0.005)
		//	{
		//		tooSmall_ = true;
		//	}
		}
	}
	TIMER_STOP("Bust grids")
//	if(tooSmall_)
//	{
//		CqString objname( "unnamed" );
//		const CqString* pattrName = pAttributes()->GetStringAttribute( "identifier", "name" );
//		if ( pattrName != 0 )
//			objname = pattrName[ 0 ];
//		Aqsis::log() << error << "Primitive \"" << objname.c_str() << "\" resulted in very small MPS (" << smallArea << ", " << bigArea << ")" << cu << ", " << cv << std::endl;
//	}

	RELEASEREF( this );
}


void CqMicroPolyGridBase::TriangleSplitPoints(CqVector3D& v1, CqVector3D& v2, TqFloat Time)
{
	// Workout where in the keyframe sequence the requested point is.
	SqTriangleSplitLine sl = m_TriangleSplitLine.GetMotionObjectInterpolated( Time );
	v1 = sl.m_TriangleSplitPoint1;
	v2 = sl.m_TriangleSplitPoint2;
}


CqMotionMicroPolyGrid::~CqMotionMicroPolyGrid()
{
	TqInt iTime;
	for ( iTime = 0; iTime < cTimes(); iTime++ )
	{
		CqMicroPolyGrid* pg = static_cast<CqMicroPolyGrid*>( GetMotionObject( Time( iTime ) ) );
		if ( NULL != pg )
			RELEASEREF( pg );
	}
}


//---------------------------------------------------------------------
/** Shade the primary grid.
 */

void CqMotionMicroPolyGrid::Shade( bool canCullGrid )
{
	CqMicroPolyGrid * pGrid = static_cast<CqMicroPolyGrid*>( GetMotionObject( Time( 0 ) ) );
	pGrid->Shade(false);
}


//---------------------------------------------------------------------
/** Transfer shader output variables for the primary grid.
 */

void CqMotionMicroPolyGrid::TransferOutputVariables()
{
	CqMicroPolyGrid * pGrid = static_cast<CqMicroPolyGrid*>( GetMotionObject( Time( 0 ) ) );
	pGrid->TransferOutputVariables();
}


//---------------------------------------------------------------------
/** Split the micropolygrid into individual MPGs,
 * \param pImage Pointer to image being rendered into.
 * \param xmin Integer minimum extend of the image part being rendered, takes into account buckets and clipping.
 * \param xmax Integer maximum extend of the image part being rendered, takes into account buckets and clipping.
 * \param ymin Integer minimum extend of the image part being rendered, takes into account buckets and clipping.
 * \param ymax Integer maximum extend of the image part being rendered, takes into account buckets and clipping.
 */

void CqMotionMicroPolyGrid::Split( CqImageBuffer* pImage, long xmin, long xmax, long ymin, long ymax )
{
	TqInt lUses = pSurface() ->Uses();
	// Get the main object, the one that was shaded.
	CqMicroPolyGrid * pGridA = static_cast<CqMicroPolyGrid*>( GetMotionObject( Time( 0 ) ) );
	TqInt cu = pGridA->uGridRes();
	TqInt cv = pGridA->vGridRes();
	TqInt iTime;

	TIMER_START("Project points")
	CqMatrix matCameraToRaster;
	QGetRenderContext() ->matSpaceToSpace( "camera", "raster", NULL, NULL, QGetRenderContext()->Time(), matCameraToRaster );
	// Check to see if this surface is single sided, if so, we can do backface culling.
	bool canBeBFCulled = ( pAttributes() ->GetIntegerAttribute( "System", "Sides" ) [ 0 ] == 1 ) && !pGridA->usesCSG();

	ADDREF( pGridA );

	// Get an array of P's for all time positions.
	std::vector<std::vector<CqVector3D> > aaPtimes;
	aaPtimes.resize( cTimes() );
	
	TqInt tTime = cTimes();
	CqMatrix matObjectToCameraT;
	register TqInt i;
	TqInt gsmin1;
	gsmin1 = pGridA->pShaderExecEnv()->shadingPointCount() - 1;

	// Store a count of backface culling for each MP, if it equals the number of timeslots, then
	// the MP can be culled.
	std::vector<TqInt> totalBFCulled;
	totalBFCulled.assign( gsmin1 + 1, 0 );

	for ( iTime = 0; iTime < tTime; iTime++ )
	{
		QGetRenderContext() ->matSpaceToSpace( "object", "camera", NULL, pSurface() ->pTransform().get(), pSurface()->pTransform()->Time(iTime), matObjectToCameraT  );
		aaPtimes[ iTime ].resize( gsmin1 + 1 );

		CqMicroPolyGrid* pg = static_cast<CqMicroPolyGrid*>( GetMotionObject( Time( iTime ) ) );
		CqVector3D* pP;
		pg->pVar(EnvVars_P) ->GetPointPtr( pP );
		CqVector3D* pNg;
		pg->pVar(EnvVars_Ng)->GetNormalPtr(pNg);
		CqVector3D* pN = NULL;
		if ( USES( lUses, EnvVars_N ) )
			pg->pVar(EnvVars_N)->GetNormalPtr(pN);

		// Cull any hidden MPs if Sides==1.  Note that this has to happen
		// *before* the transformation into hybrid camera/raster space below.
		if ( canBeBFCulled )
		{
			TIME_SCOPE("Backface culling")
			if(iTime > 0)
			{
				// The primary grid already has geometric normals computed
				// during shading, but additional grid keys don't so we need
				// to compute them here.
				pg->CalcNormals();
			}
			for ( i = gsmin1; i >= 0; i-- )
			{
				// When backface culling, we must use the geometric normal (Ng)
				// as this is the normal that properly represents the actual
				// micropolygon geometry. However, if the primitive specifies
				// custom normals as primitive variables, the direction of
				// those should be honored, in case the user has intentionally
				// switched the surface direction.  Therefore, we compare the
				// direction of Ng with that of N and flip Ng if they don't
				// match.
				TqFloat s = 1.0f;
				if(pN)
					s = (pN[i] * pNg[i] < 0.0f) ? -1.0f : 1.0f;
				if(s * pNg[i] * pP[i] >= 0)
					totalBFCulled[i]++;
			}
		}

		// Transform the whole grid to hybrid camera/raster space
		for ( i = gsmin1; i >= 0; i-- )
		{
			CqVector3D Point( pP[ i ] );

			// Make sure to retain camera space 'z' coordinate.
			TqFloat zdepth = Point.z();
			aaPtimes[ iTime ][ i ] = matCameraToRaster * Point;
			aaPtimes[ iTime ][ i ].z( zdepth );
			pP[ i ] = aaPtimes[ iTime ][ i ];
		}

		SqTriangleSplitLine sl;
		CqVector3D v0, v1, v2;
		v0 = aaPtimes[ iTime ][ 0 ];
		v1 = aaPtimes[ iTime ][ cu ];
		v2 = aaPtimes[ iTime ][ cv * ( cu + 1 ) ];
		// Check for clockwise, swap if not.
		if( ( ( v1.x() - v0.x() ) * ( v2.y() - v0.y() ) - ( v1.y() - v0.y() ) * ( v2.x() - v0.x() ) ) >= 0 )
		{
			sl.m_TriangleSplitPoint1 = v1;
			sl.m_TriangleSplitPoint2 = v2;
		}
		else
		{
			sl.m_TriangleSplitPoint1 = v2;
			sl.m_TriangleSplitPoint2 = v1;
		}
		m_TriangleSplitLine.AddTimeSlot(Time( iTime ), sl );
	}
	TIMER_STOP("Project points")

	TIMER_START("Bust grids")
	// Get the required trim curve sense, if specified, defaults to "inside".
	const CqString* pattrTrimSense = pAttributes() ->GetStringAttribute( "trimcurve", "sense" );
	CqString strTrimSense( "inside" );
	if ( pattrTrimSense != 0 )
		strTrimSense = pattrTrimSense[ 0 ];
	bool bOutside = strTrimSense == "outside";

	// Determine whether we need to bother with trimming or not.
	bool bCanBeTrimmed = pSurface() ->bCanBeTrimmed() && NULL != pGridA->pVar(EnvVars_u) && NULL != pGridA->pVar(EnvVars_v);

	TqInt iv;
	TqInt totalTimes = cTimes();
	for ( iv = 0; iv < cv; iv++ )
	{
		TqInt iu;
		for ( iu = 0; iu < cu; iu++ )
		{
			TqInt iIndex = ( iv * ( cu + 1 ) ) + iu;

			// If culled don't bother.
			if ( totalBFCulled[iIndex] == totalTimes )
			{
				STATS_INC( MPG_culled );
				continue;
			}

			// If the MPG is trimmed then don't add it.
			bool fTrimmed = false;
			if ( bCanBeTrimmed )
			{
				TqFloat u1, v1, u2, v2, u3, v3, u4, v4;

				pGridA->pVar(EnvVars_u) ->GetFloat( u1, iIndex );
				pGridA->pVar(EnvVars_v) ->GetFloat( v1, iIndex );
				pGridA->pVar(EnvVars_u) ->GetFloat( u2, iIndex + 1 );
				pGridA->pVar(EnvVars_v) ->GetFloat( v2, iIndex + 1 );
				pGridA->pVar(EnvVars_u) ->GetFloat( u3, iIndex + cu + 2 );
				pGridA->pVar(EnvVars_v) ->GetFloat( v3, iIndex + cu + 2 );
				pGridA->pVar(EnvVars_u) ->GetFloat( u4, iIndex + cu + 1 );
				pGridA->pVar(EnvVars_v) ->GetFloat( v4, iIndex + cu + 1 );

				CqVector2D vecA(u1, v1);
				CqVector2D vecB(u2, v2);
				CqVector2D vecC(u3, v3);
				CqVector2D vecD(u4, v4);

				bool fTrimA = pSurface() ->bIsPointTrimmed( vecA );
				bool fTrimB = pSurface() ->bIsPointTrimmed( vecB );
				bool fTrimC = pSurface() ->bIsPointTrimmed( vecC );
				bool fTrimD = pSurface() ->bIsPointTrimmed( vecD );

				if ( bOutside )
				{
					fTrimA = !fTrimA;
					fTrimB = !fTrimB;
					fTrimC = !fTrimC;
					fTrimD = !fTrimD;
				}

				// If all points are trimmed, need to check if the MP spans the trim curve at all, if not, then
				// we can discard it altogether.
				if ( fTrimA && fTrimB && fTrimC && fTrimD )
				{
					if(!pSurface()->bIsLineIntersecting(vecA, vecB) &&
					        !pSurface()->bIsLineIntersecting(vecB, vecC) &&
					        !pSurface()->bIsLineIntersecting(vecC, vecD) &&
					        !pSurface()->bIsLineIntersecting(vecD, vecA) )
					{
						STATS_INC( MPG_trimmedout );
						continue;
					}
				}

				// If any points are trimmed mark the MPG as needing to be trim checked.
				//fTrimmed = fTrimA || fTrimB || fTrimC || fTrimD;
				if ( fTrimA || fTrimB || fTrimC || fTrimD )
					fTrimmed = true;
			}

			CqMicroPolygonMotion *pNew = new CqMicroPolygonMotion();
			pNew->SetGrid( this );
			pNew->SetIndex( iIndex );
			for ( iTime = 0; iTime < cTimes(); iTime++ )
				pNew->AppendKey( aaPtimes[ iTime ][ iIndex ], aaPtimes[ iTime ][ iIndex + 1 ], aaPtimes[ iTime ][ iIndex + cu + 2 ], aaPtimes[ iTime ][ iIndex + cu + 1 ], Time( iTime ) );
			pImage->AddMPG( pNew );
		}
	}
	TIMER_STOP("Bust grids")

	RELEASEREF( pGridA );

	// Delete the donor motion grids, as their work is done.
	/*    for ( iTime = 1; iTime < cTimes(); iTime++ )
	    {
	        CqMicroPolyGrid* pg = static_cast<CqMicroPolyGrid*>( GetMotionObject( Time( iTime ) ) );
	        if ( NULL != pg )
	            RELEASEREF( pg );
	    }
	    //		delete( GetMotionObject( Time( iTime ) ) );
	*/
}


//---------------------------------------------------------------------
/** Default constructor
 */

CqMicroPolygon::CqMicroPolygon() : m_pGrid( 0 ), m_Flags( 0 ), m_pHitTestCache( 0 )
{
	STATS_INC( MPG_allocated );
	STATS_INC( MPG_current );
	TqInt cMPG = STATS_GETI( MPG_current );
	TqInt cPeak = STATS_GETI( MPG_peak );
	STATS_SETI( MPG_peak, cMPG > cPeak ? cMPG : cPeak );
}


//---------------------------------------------------------------------
/** Destructor
 */

CqMicroPolygon::~CqMicroPolygon()
{
	if ( m_pGrid )
		RELEASEREF( m_pGrid );
	STATS_INC( MPG_deallocated );
	STATS_DEC( MPG_current );
	if ( !IsHit() )
		STATS_INC( MPG_missed );
}


//---------------------------------------------------------------------
/** Initialise the information within the micro polygon used during sampling.
 */

void CqMicroPolygon::Initialise()
{
	// Check for degenerate case, if any of the neighbouring points are the same, shuffle them down, and
	// duplicate the last point exactly. Exact duplication of the last two points is used as a marker in the
	// fContains function to indicate degeneracy. If more that two points are coincident, we are in real trouble!
	TqInt cu = m_pGrid->uGridRes();
	TqInt IndexA = m_Index;
	TqInt IndexB = m_Index + 1;
	TqInt IndexC = m_Index + cu + 2;
	TqInt IndexD = m_Index + cu + 1;

	TqShort CodeA = 0;
	TqShort CodeB = 1;
	TqShort CodeC = 2;
	TqShort CodeD = 3;

	const CqVector3D* pP;
	m_pGrid->pVar(EnvVars_P) ->GetPointPtr( pP );
	if ( ( pP[ IndexA ] - pP[ IndexB ] ).Magnitude2() < 1e-8 )
	{
		// A--B is degenerate
		IndexB = IndexC;
		CodeB = CodeC;
		IndexC = IndexD;
		CodeC = CodeD;
		IndexD = -1;
		CodeD = -1;
	}
	else if ( ( pP[ IndexB ] - pP[ IndexC ] ).Magnitude2() < 1e-8 )
	{
		// B--C is degenerate
		IndexB = IndexC;
		CodeB = CodeC;
		IndexC = IndexD;
		CodeC = CodeD;
		IndexD = -1;
		CodeD = -1;
	}
	else if ( ( pP[ IndexC ] - pP[ IndexD ] ).Magnitude2() < 1e-8 )
	{
		// C--D is degenerate
		IndexC = IndexD;
		CodeC = CodeD;
		IndexD = -1;
		CodeD = -1;
	}
	else if ( ( pP[ IndexD ] - pP[ IndexA ] ).Magnitude2() < 1e-8 )
	{
		// D--A is degenerate
		IndexD = IndexC;
		CodeD = CodeC;
		IndexD = -1;
		CodeD = -1;
	}

	const CqVector3D& vA2 = pP[ IndexA ];
	const CqVector3D& vB2 = pP[ IndexB ];
	const CqVector3D& vC2 = pP[ IndexC ];

	// Determine whether the MPG is CW or CCW, must be CCW for fContains to work.
	bool fFlip = ( ( vA2.x() - vB2.x() ) * ( vB2.y() - vC2.y() ) ) >= ( ( vA2.y() - vB2.y() ) * ( vB2.x() - vC2.x() ) );

	m_IndexCode = 0;

	if ( !fFlip )
	{
		m_IndexCode = ( CodeD == -1 ) ?
		              ( ( CodeA & 0x3 ) | ( ( CodeC & 0x3 ) << 2 ) | ( ( CodeB & 0x3 ) << 4 ) | 0x8000000 ) :
		              ( ( CodeA & 0x3 ) | ( ( CodeD & 0x3 ) << 2 ) | ( ( CodeC & 0x3 ) << 4 ) | ( ( CodeB & 0x3 ) << 6 ) );
	}
	else
	{
		m_IndexCode = ( CodeD == -1 ) ?
		              ( ( CodeA & 0x3 ) | ( ( CodeB & 0x3 ) << 2 ) | ( ( CodeC & 0x3 ) << 4 ) | 0x8000000 ) :
		              ( ( CodeA & 0x3 ) | ( ( CodeB & 0x3 ) << 2 ) | ( ( CodeC & 0x3 ) << 4 ) | ( ( CodeD & 0x3 ) << 6 ) );
	}
}


//---------------------------------------------------------------------
/** Determinde whether the 2D point specified lies within this micropolygon.
 * \param vecP 2D vector to test for containment.
 * \param Depth Place to put the depth if valid intersection.
 * \param time The frame time at which to check containment.
 * \return Boolean indicating sample hit.
 */

bool CqMicroPolygon::fContains( const CqVector2D& vecP, TqFloat& Depth, TqFloat time) const
{
	// AGG - optimised version of above.
	TqFloat x = vecP.x(), y = vecP.y();

	// start with the edge that failed last time to get the most benefit
	// from an early exit.
	int e = m_pHitTestCache->m_LastFailedEdge;
	int prev = e - 1;
	if(prev < 0)
		prev = 3;
	for(int i=0; i<4; ++i)
	{
		// test which side of the edge the sample point lies.
		// the first two edges are tested with <= and the second two with <
		// this is so every sample point lies on exactly one side of the edge,
		// ie if it is exactly coincident with the edge it can't be on both
		// or neither sides.
		if(e & 2)
		{
			if( (( y - m_pHitTestCache->m_Y[e]) * m_pHitTestCache->m_YMultiplier[e] ) -
			        (( x - m_pHitTestCache->m_X[e]) * m_pHitTestCache->m_XMultiplier[e] ) < 0)
			{
				m_pHitTestCache->m_LastFailedEdge = e;
				return false;
			}
		}
		else
		{
			if( (( y - m_pHitTestCache->m_Y[e]) * m_pHitTestCache->m_YMultiplier[e] ) -
			        (( x - m_pHitTestCache->m_X[e]) * m_pHitTestCache->m_XMultiplier[e] ) <= 0)
			{
				m_pHitTestCache->m_LastFailedEdge = e;
				return false;
			}
		}

		// move to next edge, wrapping to zero at four.
		prev = e;
		e = (e+1) & 3;
	}

	Depth = ( m_pHitTestCache->m_D - ( m_pHitTestCache->m_VecN.x() * vecP.x() ) -
	          ( m_pHitTestCache->m_VecN.y() * vecP.y() ) ) * m_pHitTestCache->m_OneOverVecNZ;

	return true;
}

//---------------------------------------------------------------------
/** Cache some values needed for the point in poly test.
 * This must be called prior to calling fContains() on a mpg.
 */

inline void CqMicroPolygon::CacheHitTestValues(CqHitTestCache* cache, CqVector3D* points)
{
	m_pHitTestCache = cache;

	int j = 3;
	for(int i=0; i<4; ++i)
	{
		cache->m_YMultiplier[i] = points[i].x() - points[j].x();
		cache->m_XMultiplier[i] = points[i].y() - points[j].y();
		cache->m_X[i] = points[j].x();
		cache->m_Y[i] = points[j].y();
		j = i;
	}

	// if the mpg is degenerate then we repeat edge c=>a so we still have four
	// edges (it makes the test in fContains() simpler).
	if(IsDegenerate())
	{
		for(int i=2; i<4; ++i)
		{
			cache->m_YMultiplier[i] = points[3].x() - points[1].x();
			cache->m_XMultiplier[i] = points[3].y() - points[1].y();
			cache->m_X[i] = points[1].x();
			cache->m_Y[i] = points[1].y();
		}
	}

	cache->m_VecN = (points[3] - points[0]) % (points[1] - points[0]);
	cache->m_VecN.Unit();
	cache->m_D = cache->m_VecN * points[3];
	cache->m_OneOverVecNZ = 1.0 / cache->m_VecN.z();

	cache->m_LastFailedEdge = 0;
}

void CqMicroPolygon::CacheHitTestValues(CqHitTestCache* cache)
{
	CqVector3D points[4] = { PointB(), PointC(), PointD(), PointA() };
	CacheHitTestValues(cache, points);
}


// AGG - 19-6-04
// this version moves the corners of the mpg for dof.
// currently we don't use this as it's a fair bit slower than just offsetting
// the sample position in the opposite direction (we need to call this for every
// sample instead of just once). however, it is more correct than moving the
// sample because the corners may move by different amounts. if I can work out
// how to make it fast enough we should use this version.
void CqMicroPolygon::CacheHitTestValuesDof(CqHitTestCache* cache, const CqVector2D& DofOffset, CqVector2D* coc)
{
	CqVector3D points[4];
	points[0].x(PointB().x() - (coc[1].x() * DofOffset.x()));
	points[0].y(PointB().y() - (coc[1].y() * DofOffset.y()));
	points[0].z(PointB().z());
	points[1].x(PointC().x() - (coc[2].x() * DofOffset.x()));
	points[1].y(PointC().y() - (coc[2].y() * DofOffset.y()));
	points[1].z(PointC().z());
	points[2].x(PointD().x() - (coc[3].x() * DofOffset.x()));
	points[2].y(PointD().y() - (coc[3].y() * DofOffset.y()));
	points[2].z(PointD().z());
	points[3].x(PointA().x() - (coc[0].x() * DofOffset.x()));
	points[3].y(PointA().y() - (coc[0].y() * DofOffset.y()));
	points[3].z(PointA().z());

	CacheHitTestValues(cache, points);
}

void CqMicroPolygon::CacheOutputInterpCoeffs(SqMpgSampleInfo& cache) const
{
	if(cache.smoothInterpolation)
		CacheOutputInterpCoeffsSmooth(cache);
	else
		CacheOutputInterpCoeffsConstant(cache);
}

void CqMicroPolygon::InterpolateOutputs(const SqMpgSampleInfo& cache,
	const CqVector2D& pos, CqColor& outCol, CqColor& outOpac) const
{
	if(cache.smoothInterpolation)
	{
		outCol = cache.color + cache.colorMultX*pos.x() + cache.colorMultY*pos.y();
		outOpac = cache.opacity + cache.opacityMultX*pos.x() + cache.opacityMultY*pos.y();
	}
	else
	{
		outCol = cache.color;
		outOpac = cache.opacity;
	}
}

void CqMicroPolygon::CacheOutputInterpCoeffsConstant(SqMpgSampleInfo& cache) const
{
	if ( QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "Ci" ) )
	{
		cache.color = *colColor();
	}
	else
	{
		cache.color = gColWhite;
	}

	if ( QGetRenderContext() ->pDDmanager() ->fDisplayNeeds( "Oi" ) )
	{
		cache.opacity = *colOpacity();
		cache.occludes = cache.opacity >= gColWhite;
	}
	else
	{
		cache.opacity = gColWhite;
		cache.occludes = true;
	}
}

void CqMicroPolygon::CacheOutputInterpCoeffsSmooth(SqMpgSampleInfo& cache) const
{
	// Get 2D coordinates of verts in the (x,y) plane.
	CqVector2D p1(PointA());
	CqVector2D p2(PointB());
	CqVector2D p3(PointC());
	CqVector2D p4(PointD());

	// 2D diagonal vectors for the micropolygon.  The order of the vertices
	// (p1, p2, p3, p4) here winds around the micropoly, rather than taking the
	// usual order expected from RiPatch.
	CqVector2D d1 = p3 - p1;
	CqVector2D d2 = p4 - p2;
	// Center point
	CqVector2D pAvg = 0.25*(p1 + p2 + p3 + p4);

	// For each component of the colour, we compute a linear approximation to
	// the component over the micropolygon.  This is done by computing a
	// "normal" vector to a plane constructed such that the colour component
	// takes the place of the depth, "z".
	//
	// This is essentially the same method used to compute the depth of a
	// sample inside the fContains() function.

	TqFloat Nz = d1.x()*d2.y() - d1.y()*d2.x();

	if(QGetRenderContext()->pDDmanager()->fDisplayNeeds( "Ci" ))
	{
		const CqColor* pCi = NULL;
		m_pGrid->pVar(EnvVars_Ci)->GetColorPtr(pCi);

		const CqColor& c1 = pCi[GetCodedIndex(m_IndexCode, 0)];
		const CqColor& c2 = pCi[GetCodedIndex(m_IndexCode, 1)];
		const CqColor& c3 = pCi[GetCodedIndex(m_IndexCode, 2)];
		const CqColor& c4 = pCi[GetCodedIndex(m_IndexCode, 3)];
		const CqColor cAvg = 0.25*(c1 + c2 + c3 + c4);

		if(Nz != 0)
		{
			// Compute smooth shading coefficients for Ci.
			CqColor c31 = c3 - c1;
			CqColor c42 = c4 - c2;

			CqColor Nx = d1.y()*c42 - c31*d2.y();
			CqColor Ny = -d1.x()*c42 + c31*d2.x();

			TqFloat NzInv = 1/Nz;
			cache.color = (Nx*pAvg.x() + Ny*pAvg.y())*NzInv + cAvg;
			cache.colorMultX = -Nx*NzInv;
			cache.colorMultY = -Ny*NzInv;
		}
		else
		{
			// Degenerate to flat shading if the linear approx yields an
			// infinitely steep plane.
			cache.color = c1;
			cache.colorMultX = gColBlack;
			cache.colorMultY = gColBlack;
		}
	}
	else
	{
		cache.color = gColWhite;
		cache.colorMultX = gColBlack;
		cache.colorMultY = gColBlack;
	}

	if(QGetRenderContext()->pDDmanager()->fDisplayNeeds( "Oi" ))
	{
		const CqColor* pOi = NULL;
		m_pGrid->pVar(EnvVars_Oi)->GetColorPtr(pOi);

		const CqColor& o1 = pOi[GetCodedIndex(m_IndexCode, 0)];
		const CqColor& o2 = pOi[GetCodedIndex(m_IndexCode, 1)];
		const CqColor& o3 = pOi[GetCodedIndex(m_IndexCode, 2)];
		const CqColor& o4 = pOi[GetCodedIndex(m_IndexCode, 3)];
		const CqColor oAvg = 0.25*(o1 + o2 + o3 + o4);

		if(Nz != 0)
		{
			// Compute smooth shading coefficients for Oi.
			CqColor o31 = o3 - o1;
			CqColor o42 = o4 - o2;

			CqColor Nx = d1.y()*o42 - o31*d2.y();
			CqColor Ny = -d1.x()*o42 + o31*d2.x();

			TqFloat NzInv = 1/Nz;
			cache.opacity = (Nx*pAvg.x() + Ny*pAvg.y())*NzInv + oAvg;
			cache.opacityMultX = -Nx*NzInv;
			cache.opacityMultY = -Ny*NzInv;

			// The micropoly occludes if the values on all vertices do.
			cache.occludes = (o1 >= gColWhite) && (o2 >= gColWhite)
				&& (o3 >= gColWhite) && (o4 >= gColWhite);
		}
		else
		{
			cache.opacity = oAvg;
			cache.opacityMultX = gColBlack;
			cache.opacityMultY = gColBlack;
			cache.occludes = (cache.opacity >= gColWhite);
		}
	}
	else
	{
		cache.opacity = gColWhite;
		cache.opacityMultX = gColBlack;
		cache.opacityMultY = gColBlack;
		cache.occludes = true;
	}
}

CqVector2D CqMicroPolygon::ReverseBilinear( const CqVector2D& v )
{
	CqVector2D kA, kB, kC, kD;
	CqVector2D kResult;
	bool flip = false;

	kA = CqVector2D( PointA() );
	kB = CqVector2D( PointB() );
	kC = CqVector2D( PointD() );
	kD = CqVector2D( PointC() );

	if(fabs(kB.x() - kA.x()) < fabs(kC.x() - kA.x()) )
	{
		CqVector2D temp = kC;
		kC = kB;
		kB = temp;
		//flip = true;
	}

	kD += kA - kB - kC;
	kB -= kA;
	kC -= kA;

	TqFloat fBCdet = kB.x() * kC.y() - kB.y() * kC.x();
	TqFloat fCDdet = kC.y() * kD.x() - kC.x() * kD.y();

	CqVector2D kDiff = kA - v;
	TqFloat fABdet = kDiff.y() * kB.x() - kDiff.x() * kB.y();
	TqFloat fADdet = kDiff.y() * kD.x() - kDiff.x() * kD.y();
	TqFloat fA = fCDdet;
	TqFloat fB = fADdet + fBCdet;
	TqFloat fC = fABdet;

	if ( fabs( fA ) >= 1.0e-6 )
	{
		// t-equation is quadratic
		TqFloat fDiscr = sqrt( fabs( fB * fB - 4.0f * fA * fC ) );
		kResult.y( ( -fB + fDiscr ) / ( 2.0f * fA ) );
		if ( kResult.y() < 0.0f || kResult.y() > 1.0f )
		{
			kResult.y( ( -fB - fDiscr ) / ( 2.0f * fA ) );
			if ( kResult.y() < 0.0f || kResult.y() > 1.0f )
			{
				// point p not inside quadrilateral, return invalid result
				return ( CqVector2D( -1.0f, -1.0f ) );
			}
		}
	}
	else
	{
		// t-equation is linear
		kResult.y( -fC / fB );
	}
	kResult.x( -( kDiff.x() + kResult.y() * kC.x() ) / ( kB.x() + kResult.y() * kD.x() ) );
	if(flip)
	{
		TqFloat temp = kResult.x();
		kResult.x(kResult.y());
		kResult.y(temp);
	}

	return ( kResult );
}



//---------------------------------------------------------------------
/** Sample the specified point against the MPG at the specified time.
 * \param vecSample 2D vector to sample against.
 * \param time Shutter time to sample at.
 * \param D Storage for depth if valid hit.
 * \return Boolean indicating smaple hit.
 */

bool CqMicroPolygon::Sample( const SqSampleData& sample, TqFloat& D, TqFloat time, bool UsingDof )
{
	const CqVector2D& vecSample = sample.m_Position;

	// If using DoF, we need to adjust the point positions, and the hit test cache,
	// \note: this invalidates the hit test cache, but we are using DoF, so all bets are off anyway.
	// still, would be good to find out if there is a better way of doing this.
	CqHitTestCache hitTestCache;

	if(UsingDof)
	{
		CqVector3D points[4] = { PointB(), PointC(), PointD(), PointA() };
		CqVector2D coc = QGetRenderContext()->GetCircleOfConfusion(points[0].z());
		points[0].x(points[0].x() - ( coc.x() * sample.m_DofOffset.x() ));
		points[0].y(points[0].y() - ( coc.y() * sample.m_DofOffset.y() ));

		coc = QGetRenderContext()->GetCircleOfConfusion(points[1].z());
		points[1].x(points[1].x() - ( coc.x() * sample.m_DofOffset.x() ));
		points[1].y(points[1].y() - ( coc.y() * sample.m_DofOffset.y() ));

		coc = QGetRenderContext()->GetCircleOfConfusion(points[2].z());
		points[2].x(points[2].x() - ( coc.x() * sample.m_DofOffset.x() ));
		points[2].y(points[2].y() - ( coc.y() * sample.m_DofOffset.y() ));

		coc = QGetRenderContext()->GetCircleOfConfusion(points[3].z());
		points[3].x(points[3].x() - ( coc.x() * sample.m_DofOffset.x() ));
		points[3].y(points[3].y() - ( coc.y() * sample.m_DofOffset.y() ));

		CacheHitTestValues(&hitTestCache, points);
	}

	if ( fContains( vecSample, D, time ) )
	{
		// Now check if it is trimmed.
		if ( IsTrimmed() )
		{
			// Get the required trim curve sense, if specified, defaults to "inside".
			const CqString * pattrTrimSense = pGrid() ->pAttributes() ->GetStringAttribute( "trimcurve", "sense" );
			CqString strTrimSense( "inside" );
			if ( pattrTrimSense != 0 )
				strTrimSense = pattrTrimSense[ 0 ];
			bool bOutside = strTrimSense == "outside";

			CqVector2D vecUV = ReverseBilinear( vecSample );

			TqFloat u, v;

			pGrid() ->pVar(EnvVars_u) ->GetFloat( u, m_Index );
			pGrid() ->pVar(EnvVars_v) ->GetFloat( v, m_Index );
			CqVector2D uvA( u, v );

			pGrid() ->pVar(EnvVars_u) ->GetFloat( u, m_Index + 1 );
			pGrid() ->pVar(EnvVars_v) ->GetFloat( v, m_Index + 1 );
			CqVector2D uvB( u, v );

			pGrid() ->pVar(EnvVars_u) ->GetFloat( u, m_Index + pGrid() ->uGridRes() + 1 );
			pGrid() ->pVar(EnvVars_v) ->GetFloat( v, m_Index + pGrid() ->uGridRes() + 1 );
			CqVector2D uvC( u, v );

			pGrid() ->pVar(EnvVars_u) ->GetFloat( u, m_Index + pGrid() ->uGridRes() + 2 );
			pGrid() ->pVar(EnvVars_v) ->GetFloat( v, m_Index + pGrid() ->uGridRes() + 2 );
			CqVector2D uvD( u, v );

			CqVector2D vR = BilinearEvaluate( uvA, uvB, uvC, uvD, vecUV.x(), vecUV.y() );

			if ( pGrid() ->pSurface() ->bCanBeTrimmed() && pGrid() ->pSurface() ->bIsPointTrimmed( vR ) && !bOutside )
			{
				STATS_INC( MPG_trimmed );
				return ( false );
			}
		}

		if ( pGrid() ->fTriangular() )
		{
			CqVector3D vA, vB;
			pGrid()->TriangleSplitPoints( vA, vB, time );
			TqFloat Ax = vA.x();
			TqFloat Ay = vA.y();
			TqFloat Bx = vB.x();
			TqFloat By = vB.y();

			TqFloat v = ( Ay - By ) * vecSample.x() + ( Bx - Ax ) * vecSample.y() + ( Ax * By - Bx * Ay );
			if ( v <= 0 )
				return ( false );
		}

		return ( true );
	}
	else
		return ( false );
}

//---------------------------------------------------------------------
/** Calculate the 2D boundary of this micropolygon,
 */

void CqMicroPolygon::CalculateTotalBound()
{
	CqVector3D * pP;
	m_pGrid->pVar(EnvVars_P) ->GetPointPtr( pP );

	// Calculate the boundary, and store the indexes in the cache.
	const CqVector3D& B = pP[ m_Index + 1 ];
	TqInt cu = m_pGrid->uGridRes();
	const CqVector3D& C = pP[ m_Index + cu + 2 ];
	const CqVector3D& D = pP[ m_Index + cu + 1 ];

	TqShort BCMinX = 0;
	TqShort BCMaxX = 0;
	TqShort BCMinY = 0;
	TqShort BCMaxY = 0;
	TqShort BCMinZ = 0;
	TqShort BCMaxZ = 0;
	m_BoundCode = 0xe4;
	TqInt TempIndexTable[ 4 ] = {	GetCodedIndex( m_BoundCode, 0 ),
	                              GetCodedIndex( m_BoundCode, 1 ),
	                              GetCodedIndex( m_BoundCode, 2 ),
	                              GetCodedIndex( m_BoundCode, 3 ) };
	if ( B.x() < pP[ TempIndexTable[ BCMinX ] ].x() )
		BCMinX = 1;
	if ( B.x() > pP[ TempIndexTable[ BCMaxX ] ].x() )
		BCMaxX = 1;
	if ( B.y() < pP[ TempIndexTable[ BCMinY ] ].y() )
		BCMinY = 1;
	if ( B.y() > pP[ TempIndexTable[ BCMaxY ] ].y() )
		BCMaxY = 1;
	if ( B.z() < pP[ TempIndexTable[ BCMinZ ] ].z() )
		BCMinZ = 1;
	if ( B.z() > pP[ TempIndexTable[ BCMaxZ ] ].z() )
		BCMaxZ = 1;

	if ( C.x() < pP[ TempIndexTable[ BCMinX ] ].x() )
		BCMinX = 2;
	if ( C.x() > pP[ TempIndexTable[ BCMaxX ] ].x() )
		BCMaxX = 2;
	if ( C.y() < pP[ TempIndexTable[ BCMinY ] ].y() )
		BCMinY = 2;
	if ( C.y() > pP[ TempIndexTable[ BCMaxY ] ].y() )
		BCMaxY = 2;
	if ( C.z() < pP[ TempIndexTable[ BCMinZ ] ].z() )
		BCMinZ = 2;
	if ( C.z() > pP[ TempIndexTable[ BCMaxZ ] ].z() )
		BCMaxZ = 2;

	if ( !IsDegenerate() )
	{
		if ( D.x() < pP[ TempIndexTable[ BCMinX ] ].x() )
			BCMinX = 3;
		if ( D.x() > pP[ TempIndexTable[ BCMaxX ] ].x() )
			BCMaxX = 3;
		if ( D.y() < pP[ TempIndexTable[ BCMinY ] ].y() )
			BCMinY = 3;
		if ( D.y() > pP[ TempIndexTable[ BCMaxY ] ].y() )
			BCMaxY = 3;
		if ( D.z() < pP[ TempIndexTable[ BCMinZ ] ].z() )
			BCMinZ = 3;
		if ( D.z() > pP[ TempIndexTable[ BCMaxZ ] ].z() )
			BCMaxZ = 3;
	}
	m_BoundCode = ( ( BCMinX & 0x3 ) |
	                ( ( BCMinY & 0x3 ) << 2 ) |
	                ( ( BCMinZ & 0x3 ) << 4 ) |
	                ( ( BCMaxX & 0x3 ) << 6 ) |
	                ( ( BCMaxY & 0x3 ) << 8 ) |
	                ( ( BCMaxZ & 0x3 ) << 10 ) );

	m_Bound = CqBound( pP[ GetCodedIndex( m_BoundCode, 0 ) ].x(), pP[ GetCodedIndex( m_BoundCode, 1 ) ].y(), pP[ GetCodedIndex( m_BoundCode, 2 ) ].z(),
	                   pP[ GetCodedIndex( m_BoundCode, 3 ) ].x(), pP[ GetCodedIndex( m_BoundCode, 4 ) ].y(), pP[ GetCodedIndex( m_BoundCode, 5 ) ].z() );

	// Adjust for DOF
	if ( QGetRenderContext() ->UsingDepthOfField() )
	{
		const CqVector2D minZCoc = QGetRenderContext()->GetCircleOfConfusion( m_Bound.vecMin().z() );
		const CqVector2D maxZCoc = QGetRenderContext()->GetCircleOfConfusion( m_Bound.vecMax().z() );
		TqFloat cocX = max( minZCoc.x(), maxZCoc.x() );
		TqFloat cocY = max( minZCoc.y(), maxZCoc.y() );

		m_Bound.vecMin().x( m_Bound.vecMin().x() - cocX );
		m_Bound.vecMin().y( m_Bound.vecMin().y() - cocY );
		m_Bound.vecMax().x( m_Bound.vecMax().x() + cocX );
		m_Bound.vecMax().y( m_Bound.vecMax().y() + cocY );
	}
}


//---------------------------------------------------------------------
/** Calculate the 2D boundary of this micropolygon,
 */

void CqMicroPolygonMotion::CalculateTotalBound()
{
	assert( NULL != m_Keys[ 0 ] );

	m_Bound = m_Keys[ 0 ] ->GetTotalBound();
	std::vector<CqMovingMicroPolygonKey*>::iterator i;
	for ( i = m_Keys.begin(); i != m_Keys.end(); i++ )
	{
		CqBound B((*i)->GetTotalBound());
		m_Bound.Encapsulate( &B );
	}
}

//---------------------------------------------------------------------
/** Calculate a list of 2D bounds for this micropolygon,
 */
void CqMicroPolygonMotion::BuildBoundList()
{
	TqFloat opentime = QGetRenderContext() ->poptCurrent()->GetFloatOption( "System", "Shutter" ) [ 0 ];
	TqFloat closetime = QGetRenderContext() ->poptCurrent()->GetFloatOption( "System", "Shutter" ) [ 1 ];
	TqFloat shadingrate = pGrid()->GetCachedGridInfo().m_ShadingRate;

	m_BoundList.Clear();

	assert( NULL != m_Keys[ 0 ] );

	// compute an approximation of the distance travelled in raster space,
	// we use this to guide how many sub-bounds to calcuate. note, it's much
	// better for this to be fast than accurate, it's just a guide.
	TqFloat dx = fabs(m_Keys.front()->m_Point0.x() - m_Keys.back()->m_Point0.x());
	TqFloat dy = fabs(m_Keys.front()->m_Point0.y() - m_Keys.back()->m_Point0.y());
	TqUint d = static_cast<int>((dx + dy) / shadingrate) + 1; // d is always >= 1

	TqUint timeRanges = CqBucket::NumTimeRanges();
	TqUint divisions = min(d, timeRanges);
	TqFloat dt = (closetime - opentime) / divisions;
	TqFloat time = opentime + dt;
	TqInt startKey = 0;
	TqUint endKey = 1;
	CqBound bound = m_Keys[startKey]->GetTotalBound();

	m_BoundList.SetSize( divisions );

	// create a bound for each time period.
	for(TqUint i = 0; i < divisions; i++)
	{
		// find the fist key with a time greater than our end time.
		while(time > m_Times[endKey] && endKey < m_Keys.size() - 1)
			++endKey;

		// interpolate between this key and the previous to get the
		// bound at our end time.
		TqInt endKey_1 = endKey - 1;
		const CqBound& end0 = m_Keys[endKey_1]->GetTotalBound();
		TqFloat end0Time = m_Times[endKey_1];
		const CqBound& end1 = m_Keys[endKey]->GetTotalBound();
		TqFloat end1Time = m_Times[endKey];

		TqFloat mix = (time - end0Time) / (end1Time - end0Time);
		CqBound mid(end0);
		mid.vecMin() += mix * (end1.vecMin() - end0.vecMin());
		mid.vecMax() += mix * (end1.vecMax() - end0.vecMax());

		// combine with our starting bound.
		bound.Encapsulate(&mid);

		// now combine the bound with any keys that fall between our start
		// and end times.
		while(startKey < endKey_1)
		{
			startKey++;
			CqBound B(m_Keys[startKey]->GetTotalBound());
			bound.Encapsulate(&B);
		}

		m_BoundList.Set( i, bound, time - dt );

		// now set our new start to our current end ready for the next bound.
		bound = mid;
		time += dt;
	}

	m_BoundReady = true;
}


//---------------------------------------------------------------------
/** Sample the specified point against the MPG at the specified time.
 * \param vecSample 2D vector to sample against.
 * \param time Shutter time to sample at.
 * \param D Storage for depth if valid hit.
 * \return Boolean indicating smaple hit.
 */

bool CqMicroPolygonMotion::Sample( const SqSampleData& sample, TqFloat& D, TqFloat time, bool UsingDof )
{
	const CqVector2D& vecSample = sample.m_Position;
	CqHitTestCache hitTestCache;
	CqVector3D points[4];

	// Calculate the position in time of the MP.
	TqInt iIndex = 0;
	TqFloat Fraction = 0.0f;
	bool Exact = true;

	if ( time > m_Times.front() )
	{
		if ( time >= m_Times.back() )
			iIndex = m_Times.size() - 1;
		else
		{
			// Find the appropriate time span.
			iIndex = 0;
			while ( time >= m_Times[ iIndex + 1 ] )
				iIndex += 1;
			Fraction = ( time - m_Times[ iIndex ] ) / ( m_Times[ iIndex + 1 ] - m_Times[ iIndex ] );
			Exact = ( m_Times[ iIndex ] == time );
		}
	}

	// Then adjust for DoF if necessary and setup a hittestcache structure for hit testing.
	if ( Exact )
	{
		CqMovingMicroPolygonKey * pMP1 = m_Keys[ iIndex ];
		points[1] = pMP1->m_Point0;
		points[2] = pMP1->m_Point1;
		points[3] = pMP1->m_Point2;
		points[0] = pMP1->m_Point3;
	}
	else
	{
		TqFloat F1 = 1.0f - Fraction;
		CqMovingMicroPolygonKey* pMP1 = m_Keys[ iIndex ];
		CqMovingMicroPolygonKey* pMP2 = m_Keys[ iIndex + 1 ];
		points[1] = ( F1 * pMP1->m_Point0 ) + ( Fraction * pMP2->m_Point0 );
		points[2] = ( F1 * pMP1->m_Point1 ) + ( Fraction * pMP2->m_Point1 );
		points[3] = ( F1 * pMP1->m_Point2 ) + ( Fraction * pMP2->m_Point2 );
		points[0] = ( F1 * pMP1->m_Point3 ) + ( Fraction * pMP2->m_Point3 );
	}

	if(UsingDof)
	{
		CqVector2D coc = QGetRenderContext()->GetCircleOfConfusion(points[0].z());
		points[0].x(points[0].x() - ( coc.x() * sample.m_DofOffset.x() ));
		points[0].y(points[0].y() - ( coc.y() * sample.m_DofOffset.y() ));

		coc = QGetRenderContext()->GetCircleOfConfusion(points[1].z());
		points[1].x(points[1].x() - ( coc.x() * sample.m_DofOffset.x() ));
		points[1].y(points[1].y() - ( coc.y() * sample.m_DofOffset.y() ));

		coc = QGetRenderContext()->GetCircleOfConfusion(points[2].z());
		points[2].x(points[2].x() - ( coc.x() * sample.m_DofOffset.x() ));
		points[2].y(points[2].y() - ( coc.y() * sample.m_DofOffset.y() ));

		coc = QGetRenderContext()->GetCircleOfConfusion(points[3].z());
		points[3].x(points[3].x() - ( coc.x() * sample.m_DofOffset.x() ));
		points[3].y(points[3].y() - ( coc.y() * sample.m_DofOffset.y() ));
	}
	CacheHitTestValues(&hitTestCache, points);

	if ( CqMicroPolygon::fContains(vecSample, D, time) )
	{
		// Now check if it is trimmed.
		if ( IsTrimmed() )
		{
			// Get the required trim curve sense, if specified, defaults to "inside".

			/// \todo: Implement trimming of motion blurred surfaces!
		}

		if ( pGrid() ->fTriangular() )
		{
			CqVector3D vA, vB;
			pGrid()->TriangleSplitPoints( vA, vB, time );
			TqFloat Ax = vA.x();
			TqFloat Ay = vA.y();
			TqFloat Bx = vB.x();
			TqFloat By = vB.y();

			TqFloat v = ( Ay - By ) * vecSample.x() + ( Bx - Ax ) * vecSample.y() + ( Ax * By - Bx * Ay );
			if( v <= 0 )
				return ( false );
		}
		return ( true );
	}
	else
		return ( false );
}


//---------------------------------------------------------------------
/** Store the vectors of the micropolygon at the specified shutter time.
 * \param vA 3D Vector.
 * \param vB 3D Vector.
 * \param vC 3D Vector.
 * \param vD 3D Vector.
 * \param time Float shutter time that this MPG represents.
 */

void CqMicroPolygonMotion::AppendKey( const CqVector3D& vA, const CqVector3D& vB, const CqVector3D& vC, const CqVector3D& vD, TqFloat time )
{
	//	assert( time >= m_Times.back() );

	// Add a new planeset at the specified time.
	CqMovingMicroPolygonKey * pMP = new CqMovingMicroPolygonKey( vA, vB, vC, vD );
	m_Times.push_back( time );
	m_Keys.push_back( pMP );
	if ( m_Times.size() == 1 )
		m_Bound = pMP->GetTotalBound();
	else
	{
		CqBound B(pMP->GetTotalBound());
		m_Bound.Encapsulate( &B );
	}
}


//---------------------------------------------------------------------
/** Store the vectors of the micropolygon.
 * \param vA 3D Vector.
 * \param vB 3D Vector.
 * \param vC 3D Vector.
 * \param vD 3D Vector.
 */

void CqMovingMicroPolygonKey::Initialise( const CqVector3D& vA, const CqVector3D& vB, const CqVector3D& vC, const CqVector3D& vD )
{
	// Check for degenerate case, if any of the neighbouring points are the same, shuffle them down, and
	// duplicate the last point exactly. Exact duplication of the last two points is used as a marker in the
	// fContains function to indicate degeneracy. If more that two points are coincident, we are in real trouble!
	const CqVector3D & vvB = ( vA - vB ).Magnitude() < 1e-2 ? vC : vB;
	const CqVector3D& vvC = ( vvB - vC ).Magnitude() < 1e-2 ? vD : vC;
	const CqVector3D& vvD = ( vvC - vD ).Magnitude() < 1e-2 ? vvC : vD;

	// Determine whether the MPG is CW or CCW, must be CCW for fContains to work.
	bool fFlip = ( ( vA.x() - vvB.x() ) * ( vvB.y() - vvC.y() ) ) >= ( ( vA.y() - vvB.y() ) * ( vvB.x() - vvC.x() ) );

	if ( !fFlip )
	{
		m_Point0 = vA;
		m_Point1 = vvD;
		m_Point2 = vvC;
		m_Point3 = vvB;
	}
	else
	{
		m_Point0 = vA;
		m_Point1 = vvB;
		m_Point2 = vvC;
		m_Point3 = vvD;
	}

	m_N = ( vA - vvB ) % ( vvC - vvB );
	m_N.Unit();
	m_D = m_N * vA;

	m_BoundReady = false;
}


//---------------------------------------------------------------------
/** Calculate the 2D boundary of this micropolygon,
 */

const CqBound& CqMovingMicroPolygonKey::GetTotalBound()
{
	if(m_BoundReady)
		return m_Bound;

	// Calculate the boundary, and store the indexes in the cache.
	m_Bound.vecMin().x( min( m_Point0.x(), min( m_Point1.x(), min( m_Point2.x(), m_Point3.x() ) ) ) );
	m_Bound.vecMin().y( min( m_Point0.y(), min( m_Point1.y(), min( m_Point2.y(), m_Point3.y() ) ) ) );
	m_Bound.vecMin().z( min( m_Point0.z(), min( m_Point1.z(), min( m_Point2.z(), m_Point3.z() ) ) ) );
	m_Bound.vecMax().x( max( m_Point0.x(), max( m_Point1.x(), max( m_Point2.x(), m_Point3.x() ) ) ) );
	m_Bound.vecMax().y( max( m_Point0.y(), max( m_Point1.y(), max( m_Point2.y(), m_Point3.y() ) ) ) );
	m_Bound.vecMax().z( max( m_Point0.z(), max( m_Point1.z(), max( m_Point2.z(), m_Point3.z() ) ) ) );

	// Adjust for DOF
	if ( QGetRenderContext() ->UsingDepthOfField() )
	{
		const CqVector2D minZCoc = QGetRenderContext()->GetCircleOfConfusion( m_Bound.vecMin().z() );
		const CqVector2D maxZCoc = QGetRenderContext()->GetCircleOfConfusion( m_Bound.vecMax().z() );
		TqFloat cocX = max( minZCoc.x(), maxZCoc.x() );
		TqFloat cocY = max( minZCoc.y(), maxZCoc.y() );

		m_Bound.vecMin().x( m_Bound.vecMin().x() - cocX );
		m_Bound.vecMin().y( m_Bound.vecMin().y() - cocY );
		m_Bound.vecMax().x( m_Bound.vecMax().x() + cocX );
		m_Bound.vecMax().y( m_Bound.vecMax().y() + cocY );
	}

	m_BoundReady = true;
	return ( m_Bound );
}


} // namespace Aqsis
//---------------------------------------------------------------------
