void MeshPrimitive::addPrimitiveVariable( const std::string &name, const IECore::PrimitiveVariable &primVar )
{
	if ( primVar.interpolation==IECore::PrimitiveVariable::Vertex || primVar.interpolation==IECore::PrimitiveVariable::Varying )
	{
		if ( name == "P" )
		{
			// update the bounding box.
			m_memberData->bound.makeEmpty();
			IECore::ConstV3fVectorDataPtr points = IECore::runTimeCast< IECore::V3fVectorData >( primVar.data );
			if ( points )
			{
				const std::vector<Imath::V3f> &p = points->readable();
				for( unsigned int i=0; i<p.size(); i++ )
				{
					m_memberData->bound.extendBy( p[i] );
				}
			}
		}

		MemberData::ToFaceVaryingConverter primVarConverter( m_memberData->vertIds );
		// convert to facevarying
		IECore::DataPtr newData = IECore::despatchTypedData< MemberData::ToFaceVaryingConverter, IECore::TypeTraits::IsVectorTypedData >( primVar.data, primVarConverter );
		addVertexAttribute( name, newData );
	}
	else if ( primVar.interpolation==IECore::PrimitiveVariable::FaceVarying )
	{
		addVertexAttribute( name, primVar.data );
	}
	else if ( primVar.interpolation==IECore::PrimitiveVariable::Constant )
	{
		addUniformAttribute( name, primVar.data );
	}
}
bool ToMayaMeshConverter::doConversion( IECore::ConstObjectPtr from, MObject &to, IECore::ConstCompoundObjectPtr operands ) const
{
	MStatus s;

	IECore::ConstMeshPrimitivePtr mesh = IECore::runTimeCast<const IECore::MeshPrimitive>( from );
	assert( mesh );

	if ( !mesh->arePrimitiveVariablesValid() )
	{
		return false;
	}

	MFloatPointArray vertexArray;
	MIntArray polygonCounts;
	MIntArray polygonConnects;

	MFnMesh fnMesh;

	int numVertices = 0;
	IECore::PrimitiveVariableMap::const_iterator it = mesh->variables.find("P");
	if ( it != mesh->variables.end() )
	{
		/// \todo Employ some M*Array converters to simplify this
		IECore::ConstV3fVectorDataPtr p = IECore::runTimeCast<const IECore::V3fVectorData>(it->second.data);
		if (p)
		{
			numVertices = p->readable().size();

			vertexArray.setLength( numVertices );
			for (int i = 0; i < numVertices; i++)
			{
				vertexArray[i] = IECore::convert<MFloatPoint, Imath::V3f>( p->readable()[i] );
			}
		}
		else
		{
			IECore::ConstV3dVectorDataPtr p = IECore::runTimeCast<const IECore::V3dVectorData>(it->second.data);
			if (p)
			{
				numVertices = p->readable().size();

				vertexArray.setLength( numVertices );
				for (int i = 0; i < numVertices; i++)
				{
					vertexArray[i] = IECore::convert<MFloatPoint, Imath::V3d>( p->readable()[i] );
				}
			}
			else
			{
				// "P" is not convertible to an array of "points"
				return false;
			}
		}
	}


	IECore::ConstIntVectorDataPtr verticesPerFace = mesh->verticesPerFace();
	assert( verticesPerFace );
	int numPolygons = verticesPerFace->readable().size();

	polygonCounts.setLength( numPolygons );
	for (int i = 0; i < numPolygons; i++)
	{
		polygonCounts[i] = verticesPerFace->readable()[i];
	}

	IECore::ConstIntVectorDataPtr vertexIds = mesh->vertexIds();
	assert( vertexIds );
	int numPolygonConnects = vertexIds->readable().size();
	polygonConnects.setLength( numPolygonConnects );
	for (int i = 0; i < numPolygonConnects; i++)
	{
		polygonConnects[i] = vertexIds->readable()[i];
	}

	MObject mObj = fnMesh.create( numVertices, numPolygons, vertexArray, polygonCounts, polygonConnects, to, &s );

	if (!s)
	{
		return false;
	}

	it = mesh->variables.find("N");
	if ( it != mesh->variables.end() )
	{
		if (it->second.interpolation == IECore::PrimitiveVariable::FaceVarying )
		{
			/// \todo Employ some M*Array converters to simplify this
			MVectorArray vertexNormalsArray;
			IECore::ConstV3fVectorDataPtr n = IECore::runTimeCast<const IECore::V3fVectorData>(it->second.data);
			if (n)
			{
				int numVertexNormals = n->readable().size();

				vertexNormalsArray.setLength( numVertexNormals );
				for (int i = 0; i < numVertexNormals; i++)
				{
					vertexNormalsArray[i] = IECore::convert<MVector, Imath::V3f>( n->readable()[i] );
				}
			}
			else
			{
				IECore::ConstV3dVectorDataPtr n = IECore::runTimeCast<const IECore::V3dVectorData>(it->second.data);
				if (n)
				{
					int numVertexNormals = n->readable().size();

					vertexNormalsArray.setLength( numVertexNormals );
					for (int i = 0; i < numVertexNormals; i++)
					{
						vertexNormalsArray[i] = IECore::convert<MVector, Imath::V3d>( n->readable()[i] );
					}
				}
				else
				{
					IECore::msg( IECore::Msg::Warning, "ToMayaMeshConverter::doConversion", boost::format( "PrimitiveVariable \"N\" has unsupported type \"%s\"." ) % it->second.data->typeName() );
				}
			}
			
			if ( vertexNormalsArray.length() )
			{
				MStatus status;
				MItMeshPolygon itPolygon( mObj, &status );
				if( status != MS::kSuccess )
				{
					IECore::msg( IECore::Msg::Warning, "ToMayaMeshConverter::doConversion", "Failed to create mesh iterator" );
				}

				unsigned v = 0;
				MIntArray vertexIds;
				MIntArray faceIds;
				
				for ( ; !itPolygon.isDone(); itPolygon.next() )
				{
					for ( v=0; v < itPolygon.polygonVertexCount(); ++v )
					{
						faceIds.append( itPolygon.index() );
						vertexIds.append( itPolygon.vertexIndex( v ) );
					}
				}

				if( !fnMesh.setFaceVertexNormals( vertexNormalsArray, faceIds, vertexIds ) )
				{
					IECore::msg( IECore::Msg::Warning, "ToMayaMeshConverter::doConversion", "Setting normals failed" );
				}
			}
		}
		else
		{
			IECore::msg( IECore::Msg::Warning, "ToMayaMeshConverter::doConversion", "PrimitiveVariable \"N\" has unsupported interpolation (expected FaceVarying)." );
		}
	}

	bool haveDefaultUVs = false;
	IECore::PrimitiveVariableMap::const_iterator sIt = mesh->variables.find( "s" );
	IECore::RefCountedPtr sDataRef = ( sIt == mesh->variables.end() ) ? 0 : static_cast<IECore::RefCountedPtr>( sIt->second.data );

	/// Add named UV sets
	std::set< std::string > uvSets;
	for ( it = mesh->variables.begin(); it != mesh->variables.end(); ++it )
	{
		const std::string &sName = it->first;

		size_t suffixOffset = sName.rfind( "_s" );

		if ( ( suffixOffset != std::string::npos) && ( suffixOffset == sName.length() - 2 ) )
		{
			std::string uvSetNameStr = sName.substr( 0, suffixOffset );

			if ( uvSetNameStr.size() )
			{
				MString uvSetName = uvSetNameStr.c_str();
				std::string tName = uvSetNameStr + "_t";
				std::string stIdName = uvSetNameStr + "Indices";

				addUVSet( fnMesh, polygonCounts, mesh, sName, tName, stIdName, &uvSetName );

				uvSets.insert( uvSetNameStr );
				
				if ( sDataRef == static_cast<IECore::RefCountedPtr>( it->second.data ) )
				{
					haveDefaultUVs = true;
				}
			}
		}
	}

	/// Add default UV set if it isn't just a reference to a named set
	if ( !haveDefaultUVs )
	{
		addUVSet( fnMesh, polygonCounts, mesh, "s", "t", "stIndices" );
	}

	// We do the search again, but looking for primvars ending "_t", so we can catch cases where either "UVSETNAME_s" or "UVSETNAME_t" is present, but not both, taking care
	// not to attempt adding any duplicate sets
	for ( it = mesh->variables.begin(); it != mesh->variables.end(); ++it )
	{
		const std::string &tName = it->first;

		size_t suffixOffset = tName.rfind( "_t" );

		if ( ( suffixOffset != std::string::npos) && ( suffixOffset == tName.length() - 2 ) )
		{
			std::string uvSetNameStr = tName.substr( 0, suffixOffset );

			if ( uvSetNameStr.size() && uvSets.find( uvSetNameStr ) == uvSets.end() )
			{
				MString uvSetName = uvSetNameStr.c_str();
				std::string sName = uvSetNameStr + "_s";
				std::string stIdName = uvSetNameStr + "Indices";
				
				addUVSet( fnMesh, polygonCounts, mesh, sName, tName, stIdName, &uvSetName );
				uvSets.insert( uvSetNameStr );
			}
		}
	}

	/// If we're making a mesh node (rather than a mesh data) then make sure it belongs
	/// to the default shading group and add the ieMeshInterpolation attribute.
	MObject oMesh = fnMesh.object();
	if( oMesh.apiType()==MFn::kMesh )
	{
		assignDefaultShadingGroup( oMesh );
		setMeshInterpolationAttribute( oMesh, mesh->interpolation() );
	}

	/// \todo Other primvars, e.g. vertex color ("Cs")

	return true;
}
bool ToMayaCurveConverter::doConversion( IECore::ConstObjectPtr from, MObject &to, IECore::ConstCompoundObjectPtr operands ) const
{
	MStatus s;

	IECore::ConstCurvesPrimitivePtr curves = IECore::runTimeCast<const IECore::CurvesPrimitive>( from );
	
	assert( curves );

	if ( !curves->arePrimitiveVariablesValid() || !curves->numCurves() )
	{
		return false;
	}
	
	int curveIndex = indexParameter()->getNumericValue();
	if( curveIndex < 0 || curveIndex >= (int)curves->numCurves() )
	{
		IECore::msg( IECore::Msg::Warning,"ToMayaCurveConverter::doConversion",  boost::format(  "Invalid curve index \"%d\"") % curveIndex );
		return false;
	}
	
	IECore::ConstV3fVectorDataPtr p = curves->variableData< IECore::V3fVectorData >( "P", IECore::PrimitiveVariable::Vertex );
	if( !p )
	{
		IECore::msg( IECore::Msg::Warning,"ToMayaCurveConverter::doConversion",  "Curve has no \"P\" data" );
		return false;
		
	}
	
	const std::vector<int>& verticesPerCurve = curves->verticesPerCurve()->readable();
	int curveBase = 0;
	for( int i=0; i<curveIndex; ++i )
	{
		curveBase += verticesPerCurve[i];
	}
	
	MPointArray vertexArray;
	
	int numVertices = verticesPerCurve[curveIndex];
	
	int cvOffset = 0;
	if( curves->basis() != IECore::CubicBasisf::linear() && !curves->periodic() )
	{
		// Maya implicitly duplicates end points, so they're explicitly duplicated in the CurvePrimitives.
		// We need to remove those duplicates when converting back to Maya. Remove 2 cvs at start, 2 at end.
		if( numVertices < 8 )
		{
			IECore::msg( IECore::Msg::Warning,"ToMayaCurveConverter::doConversion",  "The Curve Primitive does not have enough CVs to be converted into a Maya Curve. Needs at least 8." );
			return false;
		}
		
		cvOffset = 2;
	}
	
	const std::vector<Imath::V3f>& pts = p->readable();
	
	// triple up the start points for cubic periodic curves:
	if( curves->periodic() && curves->basis() != IECore::CubicBasisf::linear() )
	{
		vertexArray.append( IECore::convert<MPoint, Imath::V3f>( pts[curveBase] ) );
		vertexArray.append( vertexArray[0] );
	}
	
	for( int i = cvOffset; i < numVertices-cvOffset; ++i )
	{
		vertexArray.append( IECore::convert<MPoint, Imath::V3f>( pts[i + curveBase] ) );
	}
	
	// if the curve is periodic, the first N cvs must be identical to the last N cvs, where N is the degree
	// of the curve:
	if( curves->periodic() )
	{
		if( curves->basis() == IECore::CubicBasisf::linear() )
		{
			// linear: N = 1
			vertexArray.append( vertexArray[0] );
		}
		else
		{
			// cubic: N = 3
			vertexArray.append( vertexArray[0] );
			vertexArray.append( vertexArray[1] );
			vertexArray.append( vertexArray[2] );
		}
	}
	
	unsigned vertexArrayLength = vertexArray.length();
	
	MDoubleArray knotSequences;
	if( curves->basis() == IECore::CubicBasisf::linear() )
	{
		for( unsigned i=0; i < vertexArrayLength; ++i )
		{
			knotSequences.append( i );
		}
	}
	else
	{
		if( curves->periodic() )
		{
			// Periodic curve, knots must be spaced out.
			knotSequences.append( -1 );
			for( unsigned i=0; i < vertexArrayLength+1; ++i )
			{
				knotSequences.append( i );
			}
		}
		else
		{
			// For a cubic curve, the first three and last three knots must be duplicated for the curve start/end to start at the first/last CV.
			knotSequences.append( 0 );
			knotSequences.append( 0 );
			for( unsigned i=0; i < vertexArrayLength-2; ++i )
			{
				knotSequences.append( i );
			}
			knotSequences.append( vertexArrayLength-3 );
			knotSequences.append( vertexArrayLength-3 );
		}
	}
	
	MFnNurbsCurve fnCurve;
	fnCurve.create( vertexArray, knotSequences, curves->basis() == IECore::CubicBasisf::linear() ? 1 : 3, curves->periodic() ? MFnNurbsCurve::kPeriodic : MFnNurbsCurve::kOpen, false, false, to, &s );
	if (!s)
	{
		IECore::msg( IECore::Msg::Warning,"ToMayaCurveConverter::doConversion",  s.errorString().asChar() );
		return false;
	}
	
	return true;
}