示例#1
0
IECore::ConstInternedStringVectorDataPtr Instancer::computeBranchChildNames( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	if( branchPath.size() == 0 )
	{
		std::string name = namePlug()->getValue();
		if( !name.size() )
		{
			return outPlug()->childNamesPlug()->defaultValue();
		}
		ConstV3fVectorDataPtr p = sourcePoints( parentPath );
		if( !p || !p->readable().size() )
		{
			return outPlug()->childNamesPlug()->defaultValue();
		}
		
		InternedStringVectorDataPtr result = new InternedStringVectorData();
		for( size_t i=0; i<p->readable().size(); i++ )
		{
			result->writable().push_back( boost::lexical_cast<string>( i ) );
		}
		
		return result;
	}
	else
	{
		ContextPtr ic = instanceContext( context, branchPath );
		Context::Scope scopedContext( ic );
		return instancePlug()->childNamesPlug()->getValue();
	}
}
示例#2
0
Imath::Box3f Instancer::computeBranchBound( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	ContextPtr ic = instanceContext( context, branchPath );
	if( ic )
	{
		Context::Scope scopedContext( ic );
		return instancePlug()->boundPlug()->getValue();
	}
	
	// branchPath == "/"
	
	Box3f result;
	ConstV3fVectorDataPtr p = sourcePoints( parentPath );
	if( p )
	{
		ScenePath branchChildPath( branchPath );
		branchChildPath.push_back( InternedString() ); // where we'll place the instance index
		for( size_t i=0; i<p->readable().size(); i++ )
		{
			/// \todo We could have a very fast InternedString( int ) constructor rather than all this lexical cast nonsense
			branchChildPath[branchChildPath.size()-1] = boost::lexical_cast<string>( i );
			Box3f branchChildBound = computeBranchBound( parentPath, branchChildPath, context );
			branchChildBound = transform( branchChildBound, computeBranchTransform( parentPath, branchChildPath, context ) );
			result.extendBy( branchChildBound );			
		}
	}

	return result;
}
示例#3
0
void Instancer::hashBranchBound( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	if( branchPath.size() <= 1 )
	{
		// "/" or "/name"

		BranchCreator::hashBranchBound( parentPath, branchPath, context, h );

		ConstV3fVectorDataPtr p = sourcePoints( parentPath );
		if( p )
		{
			p->hash( h );

			ScenePath branchChildPath( branchPath );
			if( branchChildPath.size() == 0 )
			{
				branchChildPath.push_back( namePlug()->getValue() );
			}

			BoundHash hasher( this, branchChildPath, context );
			parallel_deterministic_reduce(
				blocked_range<size_t>( 0, p->readable().size(), 100 ),
				hasher
			);

			h.append( hasher.result() );
		}
	}
	else
	{
		InstanceScope instanceScope( context, branchPath );
		h = instancePlug()->boundPlug()->hash();
	}
}
示例#4
0
Imath::Box3f Instancer::computeBranchBound( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	if( branchPath.size() <= 1 )
	{
		// "/" or "/name"
		Box3f result;
		ConstV3fVectorDataPtr p = sourcePoints( parentPath );
		if( p )
		{
			ScenePath branchChildPath( branchPath );
			if( branchChildPath.size() == 0 )
			{
				branchChildPath.push_back( namePlug()->getValue() );
			}

			BoundUnion unioner( this, branchChildPath, context, p.get() );
			parallel_reduce(
				blocked_range<size_t>( 0, p->readable().size() ),
				unioner
			);

			result = unioner.result();
		}

		return result;
	}
	else
	{
		InstanceScope instanceScope( context, branchPath );
		return instancePlug()->boundPlug()->getValue();
	}
}
示例#5
0
Imath::Box3f Primitive::bound() const
{
    Box3f result;
    PrimitiveVariableMap::const_iterator it = variables.find( "P" );
    if( it!=variables.end() )
    {
        ConstV3fVectorDataPtr p = runTimeCast<const V3fVectorData>( it->second.data );
        if( p )
        {
            const vector<V3f> &pp = p->readable();
            for( size_t i=0; i<pp.size(); i++ )
            {
                result.extendBy( pp[i] );
            }
        }
    }
    return result;
}
示例#6
0
Imath::M44f Instancer::computeBranchTransform( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	if( branchPath.size() < 2 )
	{
		// "/" or "/name"
		return M44f();
	}
	else if( branchPath.size() == 2 )
	{
		// "/name/instanceNumber"
		int index = instanceIndex( branchPath );
		ConstV3fVectorDataPtr p = sourcePoints( parentPath );
		return instanceTransform( p.get(), index );
	}
	else
	{
		InstanceScope instanceScope( context, branchPath );
		return instancePlug()->transformPlug()->getValue();
	}
}
示例#7
0
NURBSPrimitive::NURBSPrimitive( int uOrder, ConstFloatVectorDataPtr uKnot, float uMin, float uMax,
	int vOrder, ConstFloatVectorDataPtr vKnot, float vMin, float vMax, ConstV3fVectorDataPtr p )
{
	setTopology( uOrder, uKnot, uMin, uMax, vOrder, vKnot, vMin, vMax );
	if( p )
	{
		V3fVectorDataPtr pData = p->copy();
		pData->setInterpretation( GeometricData::Point );
		variables.insert( PrimitiveVariableMap::value_type( "P", PrimitiveVariable( PrimitiveVariable::Vertex, pData ) ) );
	}
}
示例#8
0
CurvesPrimitive::CurvesPrimitive( ConstIntVectorDataPtr vertsPerCurve, const CubicBasisf &basis, bool periodic, ConstV3fVectorDataPtr p )
	: 	m_basis( CubicBasisf::linear() )
{
	setTopology( vertsPerCurve, basis, periodic );

	if( p )
	{
		V3fVectorDataPtr pData = p->copy();
		pData->setInterpretation( GeometricData::Point );
		variables["P"] = PrimitiveVariable( PrimitiveVariable::Vertex, pData );
	}
}
示例#9
0
IECore::ConstInternedStringVectorDataPtr Instancer::computeBranchChildNames( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	if( branchPath.size() == 0 )
	{
		// "/"
		std::string name = namePlug()->getValue();
		if( name.empty() )
		{
			return outPlug()->childNamesPlug()->defaultValue();
		}
		InternedStringVectorDataPtr result = new InternedStringVectorData();
		result->writable().push_back( name );
		return result;
	}
	else if( branchPath.size() == 1 )
	{
		ConstV3fVectorDataPtr p = sourcePoints( parentPath );
		if( !p || !p->readable().size() )
		{
			return outPlug()->childNamesPlug()->defaultValue();
		}

		const size_t s = p->readable().size();
		InternedStringVectorDataPtr resultData = new InternedStringVectorData();
		vector<InternedString> &result = resultData->writable();
		result.resize( s );

		for( size_t i = 0; i < s ; ++i )
		{
			result[i] = InternedString( i );
		}

		return resultData;
	}
	else
	{
		InstanceScope instanceScope( context, branchPath );
		return instancePlug()->childNamesPlug()->getValue();
	}
}
示例#10
0
Imath::M44f Instancer::computeBranchTransform( const ScenePath &parentPath, const ScenePath &branchPath, const Gaffer::Context *context ) const
{
	M44f result;
	ContextPtr ic = instanceContext( context, branchPath );
	if( ic )
	{
		Context::Scope scopedContext( ic );
		result = instancePlug()->transformPlug()->getValue();
	}
	
	if( branchPath.size() == 1 )
	{
		int index = instanceIndex( branchPath );
		ConstV3fVectorDataPtr p = sourcePoints( parentPath );
		if( p && (size_t)index < p->readable().size() )
		{
			M44f t;
			t.translate( p->readable()[index] );
			result *= t;
		}
	}
	return result;
}
示例#11
0
SimpleSubsurface::SimpleSubsurface( ConstV3fVectorDataPtr p, ConstColor3fVectorDataPtr c, ConstFloatVectorDataPtr a, const SplinefColor3f &falloff )
{
	m_privateData = boost::shared_ptr<PrivateData>( new PrivateData );
	
	m_privateData->points = p->copy();
	m_privateData->colors = c->copy();
	
	const vector<float> &areas = a->readable();
	vector<Color3f> &colors = m_privateData->colors->writable();
	for( size_t i=0; i<colors.size(); i++ )
	{
		colors[i] *= areas[i];
	}
	
	m_privateData->tree.init( m_privateData->points->readable().begin(), m_privateData->points->readable().end() );

	m_privateData->falloff.init( SplineRemapper( falloff ), 0, 1, 100 );
	
	m_privateData->nodeCentroids.resize( m_privateData->tree.numNodes() );
	m_privateData->nodeColors.resize( m_privateData->tree.numNodes() );
	m_privateData->nodeBounds.resize( m_privateData->tree.numNodes() );
	buildWalk( m_privateData->tree.rootIndex() );
	
}
void MeshPrimitiveImplicitSurfaceOp::modifyTypedPrimitive( MeshPrimitive * typedPrimitive, const CompoundObject * operands )
{
	const float threshold = m_thresholdParameter->getNumericValue();

	bool automaticBound = static_cast<const BoolData *>( m_automaticBoundParameter->getValue() )->readable();
	Box3f bound;

	if (automaticBound)
	{
		bound.makeEmpty();

		PrimitiveVariableMap::const_iterator it = typedPrimitive->variables.find("P");

		if (it != typedPrimitive->variables.end())
		{
			const DataPtr &verticesData = it->second.data;

			/// \todo Use depatchTypedData
			if (runTimeCast<V3fVectorData>(verticesData))
			{
				ConstV3fVectorDataPtr p = runTimeCast<V3fVectorData>(verticesData);

				for ( V3fVectorData::ValueType::const_iterator it = p->readable().begin();
					it != p->readable().end(); ++it)
				{
					bound.extendBy( *it );
				}
			}
			else if (runTimeCast<V3dVectorData>(verticesData))
			{
				ConstV3dVectorDataPtr p = runTimeCast<V3dVectorData>(verticesData);

				for ( V3dVectorData::ValueType::const_iterator it = p->readable().begin();
					it != p->readable().end(); ++it)
				{
					bound.extendBy( *it );
				}
			}
			else
			{
				throw InvalidArgumentException("MeshPrimitive has no primitive variable \"P\" of type V3fVectorData/V3dVectorData in MeshPrimitiveImplicitSurfaceOp");
			}
		}
		else
		{
			throw InvalidArgumentException("MeshPrimitive has no primitive variable \"P\" in MeshPrimitiveImplicitSurfaceOp");
		}
	}
	else
	{
		bound = static_cast<const Box3fData *>( m_boundParameter->getValue() )->readable();
	}

	float boundExtend = m_boundExtendParameter->getNumericValue();
	bound.min -= V3f( boundExtend, boundExtend, boundExtend );
	bound.max += V3f( boundExtend, boundExtend, boundExtend );


	V3i resolution;
	int gridMethod = m_gridMethodParameter->getNumericValue();
	if ( gridMethod == Resolution )
	{
		resolution = static_cast<const V3iData *>( m_resolutionParameter->getValue() )->readable();
	}
	else if ( gridMethod == DivisionSize )
	{
		V3f divisionSize = static_cast<const V3fData *>( m_divisionSizeParameter->getValue() )->readable();

		resolution.x = (int)((bound.max.x - bound.min.x) / divisionSize.x);
		resolution.y = (int)((bound.max.y - bound.min.y) / divisionSize.y);
		resolution.z = (int)((bound.max.z - bound.min.z) / divisionSize.z);

	}
	else
	{
		assert( false );
	}


	resolution.x = std::max( 1, resolution.x );
	resolution.y = std::max( 1, resolution.y );
	resolution.z = std::max( 1, resolution.z );

	/// Calculate a tolerance which is half the size of the smallest grid division
	double cacheTolerance = ((bound.max.x - bound.min.x) / (double)resolution.x) / 2.0;
	cacheTolerance = std::min(cacheTolerance, ((bound.max.y - bound.min.y) / (double)resolution.y) / 2.0 );
	cacheTolerance = std::min(cacheTolerance, ((bound.max.z - bound.min.z) / (double)resolution.z) / 2.0 );

	MeshPrimitiveBuilderPtr builder = new MeshPrimitiveBuilder();

	typedef MarchingCubes< CachedImplicitSurfaceFunction< V3f, float > > Marcher ;

	MeshPrimitiveImplicitSurfaceFunctionPtr fn = new MeshPrimitiveImplicitSurfaceFunction( typedPrimitive );

	Marcher::Ptr m = new Marcher
	(
		new CachedImplicitSurfaceFunction< V3f, float >(
			fn,
			cacheTolerance
		),

		builder
	);

	m->march( Box3f( bound.min, bound.max ), resolution, threshold );
	MeshPrimitivePtr resultMesh = builder->mesh();
	typedPrimitive->variables.clear();

	typedPrimitive->setTopology(
		resultMesh->verticesPerFace(),
		resultMesh->vertexIds()
	);

	typedPrimitive->variables["P"] = PrimitiveVariable( resultMesh->variables["P"].interpolation, resultMesh->variables["P"].data->copy() );
	typedPrimitive->variables["N"] = PrimitiveVariable( resultMesh->variables["N"].interpolation, resultMesh->variables["N"].data->copy() );

}
示例#13
0
void FaceAreaOp::modifyTypedPrimitive( MeshPrimitive * mesh, const CompoundObject * operands )
{
	string areaPrimVarName = parameters()->parameter<StringParameter>( "areaPrimVar" )->getTypedValue();
	if( areaPrimVarName!="" )
	{
	
		const string &pName = parameters()->parameter<StringParameter>( "pointPrimVar" )->getTypedValue();
		ConstV3fVectorDataPtr pData = mesh->variableData<V3fVectorData>( pName, PrimitiveVariable::Vertex );
		if( !pData )
		{
			throw InvalidArgumentException( boost::str( boost::format( "FaceAreaOp : MeshPrimitive has no \"%s\" primitive variable." ) % pName ) );
		}
		const vector<V3f> &p = pData->readable();

		FloatVectorDataPtr areasData = new FloatVectorData;
		vector<float> &areas = areasData->writable();
		areas.reserve( mesh->variableSize( PrimitiveVariable::Uniform ) );
		PolygonIterator faceEnd = mesh->faceEnd();
		for( PolygonIterator pIt = mesh->faceBegin(); pIt!=faceEnd; pIt++ )
		{
			typedef vector<V3f> PointVector;
			areas.push_back( polygonArea( pIt.vertexBegin( p.begin() ), pIt.vertexEnd( p.begin() ) ) );
		}

		mesh->variables[areaPrimVarName] = PrimitiveVariable( PrimitiveVariable::Uniform, areasData );
	}
	
	string textureAreaPrimVarName = parameters()->parameter<StringParameter>( "textureAreaPrimVar" )->getTypedValue();
	if( textureAreaPrimVarName!="" )
	{
	
		const string &sName = parameters()->parameter<StringParameter>( "sPrimVar" )->getTypedValue();
		PrimitiveVariable::Interpolation sInterpolation = PrimitiveVariable::Vertex;
		ConstFloatVectorDataPtr sData = mesh->variableData<FloatVectorData>( sName, PrimitiveVariable::Vertex );
		if( !sData )
		{
			sData = mesh->variableData<FloatVectorData>( sName, PrimitiveVariable::FaceVarying );
			if( !sData )
			{
				throw InvalidArgumentException( boost::str( boost::format( "FaceAreaOp : MeshPrimitive has no suitable \"%s\" primitive variable." ) % sName ) );
			}
			sInterpolation = PrimitiveVariable::FaceVarying;
		}
		const vector<float> &s = sData->readable();

		const string &tName = parameters()->parameter<StringParameter>( "tPrimVar" )->getTypedValue();
		PrimitiveVariable::Interpolation tInterpolation = PrimitiveVariable::Vertex;
		ConstFloatVectorDataPtr tData = mesh->variableData<FloatVectorData>( tName, PrimitiveVariable::Vertex );
		if( !tData )
		{
			tData = mesh->variableData<FloatVectorData>( tName, PrimitiveVariable::FaceVarying );
			if( !tData )
			{
				throw InvalidArgumentException( boost::str( boost::format( "FaceAreaOp : MeshPrimitive has no suitable \"%s\" primitive variable." ) % tName ) );
			}
			tInterpolation = PrimitiveVariable::FaceVarying;
		}
		const vector<float> &t = tData->readable();

		if( sInterpolation!=tInterpolation )
		{
			throw InvalidArgumentException( boost::str( boost::format( "FaceAreaOp : interpolation for \"%s\" and \"%s\" primitive variables don't match." ) % sName % tName ) );
		}

		FloatVectorDataPtr textureAreasData = new FloatVectorData;
		vector<float> &textureAreas = textureAreasData->writable();
		textureAreas.reserve( mesh->variableSize( PrimitiveVariable::Uniform ) );
		PolygonIterator faceEnd = mesh->faceEnd();
		for( PolygonIterator pIt = mesh->faceBegin(); pIt!=faceEnd; pIt++ )
		{
			if( sInterpolation==PrimitiveVariable::Vertex )
			{
				typedef PolygonVertexIterator<vector<float>::const_iterator> VertexIterator;
				typedef boost::tuple<VertexIterator, VertexIterator> IteratorTuple;
				typedef boost::zip_iterator<IteratorTuple> ZipIterator;
				typedef boost::transform_iterator<STTupleToV3f, ZipIterator> STIterator;
			
				STIterator begin( ZipIterator( IteratorTuple( pIt.vertexBegin( s.begin() ), pIt.vertexBegin( t.begin() ) ) ) );
				STIterator end( ZipIterator( IteratorTuple( pIt.vertexEnd( s.begin() ), pIt.vertexEnd( t.begin() ) ) ) );
			
				textureAreas.push_back( polygonArea( begin, end ) );
			}
			else
			{
				assert( sInterpolation==PrimitiveVariable::FaceVarying );
				typedef boost::tuple<vector<float>::const_iterator, vector<float>::const_iterator> IteratorTuple;
				typedef boost::zip_iterator<IteratorTuple> ZipIterator;
				typedef boost::transform_iterator<STTupleToV3f, ZipIterator> STIterator;
			
				STIterator begin( ZipIterator( IteratorTuple( pIt.faceVaryingBegin( s.begin() ), pIt.faceVaryingBegin( t.begin() ) ) ) );
				STIterator end( ZipIterator( IteratorTuple( pIt.faceVaryingEnd( s.begin() ), pIt.faceVaryingEnd( t.begin() ) ) ) );
			
				textureAreas.push_back( polygonArea( begin, end ) );
			}
		}

		mesh->variables[textureAreaPrimVarName] = PrimitiveVariable( PrimitiveVariable::Uniform, textureAreasData );
	
	}
	
}
示例#14
0
PatchMeshPrimitivePtr CurveExtrudeOp::buildPatchMesh( const CurvesPrimitive * curves, unsigned curveIndex, unsigned vertexOffset, unsigned varyingOffset ) const
{
	if ( curves->periodic() )
	{
		throw InvalidArgumentException( "CurveExtrudeOp: Cannot convert periodic curves" );
	}

	PrimitiveVariableMap::const_iterator it = curves->variables.find( "P" );
	if ( it == curves->variables.end() )
	{
		throw InvalidArgumentException( "CurveExtrudeOp: Input curve has no 'P' primvar" );
	}
	ConstV3fVectorDataPtr pData = runTimeCast< const V3fVectorData >( it->second.data );
	if ( !pData )
	{
		throw InvalidArgumentException( "CurveExtrudeOp: Input curve has no 'P' primvar of type V3fVectorData" );
	}

	float width = 1.0f;
	it = curves->variables.find( "constantwidth" );
	if ( it != curves->variables.end() )
	{
		ConstFloatDataPtr widthData = 0;
		if ( it->second.interpolation == PrimitiveVariable::Constant )
		{
			widthData = runTimeCast< const FloatData >( it->second.data );
		}

		if ( widthData )
		{
			width = widthData->readable();
		}
		else
		{
			msg( Msg::Warning, "CurveExtrudeOp", "Ignoring malformed primvar 'constantwidth'" );
		}
	}

	ConstFloatVectorDataPtr varyingWidthData = 0;
	ConstFloatVectorDataPtr vertexWidthData = 0;
	it = curves->variables.find( "width" );
	if ( it != curves->variables.end() )
	{
		if ( it->second.interpolation == PrimitiveVariable::Varying )
		{
			varyingWidthData = runTimeCast< const FloatVectorData >( it->second.data );
		}
		else if ( it->second.interpolation == PrimitiveVariable::Vertex )
		{
			vertexWidthData = runTimeCast< const FloatVectorData >( it->second.data );
		}

		if ( !varyingWidthData && !vertexWidthData)
		{
			msg( Msg::Warning, "CurveExtrudeOp", "Ignoring malformed primvar 'width'" );
		}
	}

	const V2i &resolution = m_resolutionParameter->getTypedValue();

	const unsigned int vPoints = resolution.y;
	const unsigned int uPoints = resolution.x;

	PatchMeshPrimitivePtr patchMesh = new PatchMeshPrimitive(
		uPoints,
		vPoints + 2, // End points are duplicated
		CubicBasisf::catmullRom(),
		CubicBasisf::catmullRom(),
		true,
		false
	);

	for ( PrimitiveVariableMap::const_iterator it = curves->variables.begin(); it != curves->variables.end(); ++it )
	{
		if ( it->second.interpolation == PrimitiveVariable::FaceVarying || it->second.interpolation == PrimitiveVariable::Varying )
		{
			VaryingFn varyingFn( it->first, curves, curveIndex, varyingOffset, resolution );
 			assert( it->second.data );

			patchMesh->variables[ it->first ] = PrimitiveVariable(
				it->second.interpolation,
				despatchTypedData<VaryingFn, TypeTraits::IsStrictlyInterpolableVectorTypedData>( it->second.data, varyingFn )
			);

		}
		else if ( it->second.interpolation == PrimitiveVariable::Vertex )
		{
			VertexFn vertexFn( it->first, curves, curveIndex, vertexOffset, resolution );
 			assert( it->second.data );

			patchMesh->variables[ it->first ] = PrimitiveVariable(
				it->second.interpolation,
				despatchTypedData<VertexFn, TypeTraits::IsStrictlyInterpolableVectorTypedData>( it->second.data, vertexFn )
			);

		}
		else if ( it->second.interpolation == PrimitiveVariable::Constant )
		{
			patchMesh->variables[ it->first ] = PrimitiveVariable( it->second.interpolation, it->second.data->copy() );
		}
		else if ( it->second.interpolation == PrimitiveVariable::Uniform )
		{
			UniformFn uniformFn( it->first, curves, curveIndex );
			patchMesh->variables[ it->first ] = PrimitiveVariable(
				PrimitiveVariable::Constant,
				despatchTypedData<UniformFn, TypeTraits::IsVectorTypedData>( it->second.data, uniformFn )
			);
		}
	}

	if ( varyingWidthData )
	{
		assert( !vertexWidthData );
		PrimitiveVariableMap::const_iterator it = patchMesh->variables.find( "width" );
		assert( it !=  patchMesh->variables.end() );

		varyingWidthData = runTimeCast< const FloatVectorData >( it->second.data );
		assert( varyingWidthData );
	}
	else if ( vertexWidthData )
	{
		PrimitiveVariableMap::const_iterator it = patchMesh->variables.find( "width" );
		assert( it !=  patchMesh->variables.end() );

		vertexWidthData = runTimeCast< const FloatVectorData >( it->second.data );
		assert( vertexWidthData );
	}

	const V3fVectorData::ValueType &p = pData->readable();

	V3fVectorData::ValueType resampledPoints;
	resampledPoints.reserve( vPoints );

	V3fVectorData::ValueType resampledTangents;
	resampledPoints.reserve( vPoints );

	/// \todo Make adaptive
	for ( unsigned v = 0; v < vPoints; v ++)
	{
		size_t iSeg;
		float fSeg;

		/// Make sure we don't fall off the end of the curve
		if ( v == vPoints - 1 )
		{
			iSeg = curves->numSegments( curveIndex ) - 1;
			fSeg = 1.0f - std::numeric_limits<float>::epsilon();
		}
		else
		{
			float curveParam = float(v) / ( vPoints - 1 );
			fSeg = curveParam * curves->numSegments( curveIndex );
			iSeg = (size_t)floor( fSeg );
			fSeg = fSeg - iSeg;
		}

		size_t segmentStart = iSeg;

		size_t i0 = std::min( segmentStart + 0, curves->variableSize( PrimitiveVariable::Vertex, curveIndex ) );
		size_t i1 = std::min( segmentStart + 1, curves->variableSize( PrimitiveVariable::Vertex, curveIndex ) );
		size_t i2 = std::min( segmentStart + 2, curves->variableSize( PrimitiveVariable::Vertex, curveIndex ) );
		size_t i3 = std::min( segmentStart + 3, curves->variableSize( PrimitiveVariable::Vertex, curveIndex ) );

		const Imath::V3f &p0 = p[ vertexOffset + i0 ];
		const Imath::V3f &p1 = p[ vertexOffset + i1  ];
		const Imath::V3f &p2 = p[ vertexOffset + i2  ];
		const Imath::V3f &p3 = p[ vertexOffset + i3  ];

		Imath::V3f pt = curves->basis()(
				fSeg,
				p0, p1, p2, p3
			);

		resampledPoints.push_back( pt );

		resampledTangents.push_back(
			curves->basis().derivative(
				fSeg,
				p0, p1, p2, p3
			).normalized()
		);

	}
	assert( resampledPoints.size() == vPoints );
	assert( resampledTangents.size() == vPoints );

	std::vector< M44f > frames;

	buildReferenceFrames( resampledPoints, resampledTangents, frames );
	assert( frames.size() == vPoints );

	std::vector< V3f > patchP;
	patchP.reserve( uPoints * ( vPoints + 2 ) );

	for ( unsigned int v = 0; v < vPoints; v++ )
	{
		if ( varyingWidthData )
		{
			assert( !vertexWidthData );
			assert( v * uPoints <  varyingWidthData->readable().size() );
			width = varyingWidthData->readable()[v * uPoints];
		}
		else if ( vertexWidthData )
		{
			assert( (v+1) * uPoints <  vertexWidthData->readable().size() );
			width = vertexWidthData->readable()[(v+1) * uPoints];
		}

		const float radius = width / 2.0f;

		/// Double up end points
		const int num = v == 0 || v == vPoints - 1 ? 2 : 1;

		for ( int x = 0; x < num; x++)
		{
			for( unsigned int u = 0; u < uPoints; u++ )
			{
				/// We're periodic in 'u', so no need to close the curve.
				/// Go from -PI to PI, in order to make the periodicity work, and to give the
				/// surface the correct orientation.
				float theta = -2.0 * M_PI * float(u) / float(uPoints) - M_PI;

				V3f circlePoint(
					0.0,
					radius * cos( theta ),
					radius * sin( theta )
				);

				circlePoint = circlePoint * frames[v];

				patchP.push_back( circlePoint );
			}
		}
	}

	patchMesh->variables["P"] = PrimitiveVariable( PrimitiveVariable::Vertex, new V3fVectorData( patchP ) );

	assert( patchMesh->arePrimitiveVariablesValid() );

	return patchMesh;
}