OP_ERROR SOP_SceneCacheSource::cookMySop( OP_Context &context )
{
	flags().setTimeDep( true );
	
	std::string file;
	if ( !ensureFile( file ) )
	{
		addError( SOP_ATTRIBUTE_INVALID, ( file + " is not a valid .scc" ).c_str() );
		gdp->clearAndDestroy();
		return error();
	}
	
	std::string path = getPath();
	Space space = getSpace();
	
	UT_String shapeFilterStr;
	evalString( shapeFilterStr, pShapeFilter.getToken(), 0, 0 );
	UT_StringMMPattern shapeFilter;
	shapeFilter.compile( shapeFilterStr );
	
	UT_String p( "P" );
	UT_String attributeFilter;
	evalString( attributeFilter, pAttributeFilter.getToken(), 0, 0 );
	if ( !p.match( attributeFilter ) )
	{
		attributeFilter += " P";
	}
	
	ConstSceneInterfacePtr scene = this->scene( file, path );
	if ( !scene )
	{
		addError( SOP_ATTRIBUTE_INVALID, ( path + " is not a valid location in " + file ).c_str() );
		gdp->clearAndDestroy();
		return error();
	}
	
	MurmurHash hash;
	hash.append( file );
	hash.append( path );
	hash.append( space );
	hash.append( shapeFilterStr );
	hash.append( attributeFilter );
	
	if ( !m_loaded || m_hash != hash )
	{
		gdp->clearAndDestroy();
	}
	
	Imath::M44d transform = ( space == World ) ? worldTransform( file, path, context.getTime() ) : Imath::M44d();
	
	SceneInterface::Path rootPath;
	scene->path( rootPath );
	
	loadObjects( scene, transform, context.getTime(), space, shapeFilter, attributeFilter.toStdString(), rootPath.size() );
	
	m_loaded = true;
	m_hash = hash;
	
	return error();
}
int ROP_SceneCacheWriter::startRender( int nframes, fpreal s, fpreal e )
{
	UT_String value;
	evalString( value, pRootObject.getToken(), 0, 0 );
	
	try
	{
		SceneInterface::Path emptyPath;
		m_liveScene = new IECoreHoudini::HoudiniScene( value, emptyPath, emptyPath );
	}
	catch ( IECore::Exception &e )
	{		
		addError( ROP_MESSAGE, e.what() );
		return false;
	}
	
	evalString( value, pFile.getToken(), 0, 0 );
	std::string file = value.toStdString();
	
	try
	{
		m_outScene = SceneInterface::create( file, IndexedIO::Write );
	}
	catch ( IECore::Exception &e )
	{
		addError( ROP_MESSAGE, ( "Could not create a writable IECore::SceneInterface at \"" + file + "\"" ).c_str() );
		return false;
	}
	
	return true;
}
Beispiel #3
0
std::string getStringParam(OP_Parameters& node, OP_Context &context,
	const std::string& label, bool trimspace)
{
	UT_String s;
	node.evalString(s, label.c_str(), 0, context.getTime());
	if(trimspace)
		s.trimSpace();

	return s.toStdString();
}
Beispiel #4
0
std::string
GusdGetErrors(UT_ErrorManager* mgr, UT_ErrorSeverity sev)
{
    std::string err;
    if(mgr && mgr->getSeverity() >= SYSmin(sev, UT_ERROR_MESSAGE)) {
        UT_String msg;
        mgr->getErrorMessages(msg, sev, /*headerflag*/ 0);
        err = msg.toStdString();
    }
    return err;
}
OP_ERROR SOP_CortexConverter::cookMySop( OP_Context &context )
{
	if( lockInputs( context ) >= UT_ERROR_ABORT )
	{
		return error();
	}
	
	UT_Interrupt *boss = UTgetInterrupt();
	boss->opStart("Building CortexConverter Geometry...");
	gdp->clearAndDestroy();
	
	UT_String nameFilterStr;
	evalString( nameFilterStr, pNameFilter.getToken(), 0, 0 );
	UT_StringMMPattern nameFilter;
	nameFilter.compile( nameFilterStr );
	
	UT_String p( "P" );
	UT_String attributeFilter;
	evalString( attributeFilter, pAttributeFilter.getToken(), 0, 0 );
	if ( !p.match( attributeFilter ) )
	{
		attributeFilter += " P";
	}
	const std::string attributeFilterStr = attributeFilter.toStdString();
	
	ResultType type = (ResultType)this->evalInt( pResultType.getToken(), 0, 0 );
	bool convertStandardAttributes = evalInt( pConvertStandardAttributes.getToken(), 0, 0 );
	
	DetailSplitterPtr splitter = new DetailSplitter( inputGeoHandle( 0 ) );
	std::vector<std::string> names;
	splitter->values( names );
	for ( std::vector<std::string>::const_iterator it = names.begin(); it != names.end(); ++it )
	{
		const std::string &name = *it;
		
		// we want match all to also match no-name
		if ( UT_String( name ).multiMatch( nameFilter ) || ( name == "" && UT_String( "*" ).multiMatch( nameFilter ) ) )
		{
			doConvert( splitter->split( name ), name, type, attributeFilterStr, convertStandardAttributes );
		}
		else
		{
			doPassThrough( splitter->split( name ), name );
		}
	}
	
	boss->opEnd();
	unlockInputs();
	return error();
}
OP_ERROR SOP_SceneCacheSource::cookMySop( OP_Context &context )
{
	// make sure the state is valid
	if ( boost::indeterminate( m_static ) )
	{
		sceneChanged();
	}
	
	flags().setTimeDep( bool( !m_static ) );
	
	std::string file;
	if ( !ensureFile( file ) )
	{
		addError( SOP_ATTRIBUTE_INVALID, ( file + " is not a valid .scc" ).c_str() );
		gdp->clearAndDestroy();
		return error();
	}
	
	std::string path = getPath();
	Space space = getSpace();
	GeometryType geometryType = (GeometryType)this->evalInt( pGeometryType.getToken(), 0, 0 );
	
	UT_String shapeFilterStr;
	evalString( shapeFilterStr, pShapeFilter.getToken(), 0, 0 );
	UT_StringMMPattern shapeFilter;
	shapeFilter.compile( shapeFilterStr );
	
	UT_String p( "P" );
	UT_String attributeFilter;
	evalString( attributeFilter, pAttributeFilter.getToken(), 0, 0 );
	if ( !p.match( attributeFilter ) )
	{
		attributeFilter += " P";
	}
	
	ConstSceneInterfacePtr scene = this->scene( file, path );
	if ( !scene )
	{
		addError( SOP_ATTRIBUTE_INVALID, ( path + " is not a valid location in " + file ).c_str() );
		gdp->clearAndDestroy();
		return error();
	}
	
	MurmurHash hash;
	hash.append( file );
	hash.append( path );
	hash.append( space );
	hash.append( shapeFilterStr );
	hash.append( attributeFilter );
	hash.append( geometryType );
	hash.append( getObjectOnly() );
	
	if ( !m_loaded || m_hash != hash )
	{
		gdp->clearAndDestroy();
	}
	
	Imath::M44d transform = ( space == World ) ? worldTransform( file, path, context.getTime() ) : Imath::M44d();
	
	SceneInterface::Path rootPath;
	scene->path( rootPath );
	
	UT_Interrupt *progress = UTgetInterrupt();
	if ( !progress->opStart( ( "Cooking objects for " + getPath() ).c_str() ) )
	{
		addError( SOP_ATTRIBUTE_INVALID, "Cooking interrupted before it started" );
		gdp->clearAndDestroy();
		return error();
	}
	
	loadObjects( scene, transform, context.getTime(), space, shapeFilter, attributeFilter.toStdString(), geometryType, rootPath.size() );
	
	if ( progress->opInterrupt( 100 ) )
	{
		addError( SOP_ATTRIBUTE_INVALID, "Cooking interrupted" );
		gdp->clearAndDestroy();		
		m_loaded = false;
		m_hash = MurmurHash();
	}
	else
	{
		m_loaded = true;
		m_hash = hash;
	}
	
	progress->opEnd();
	
	return error();
}
OP_ERROR SOP_AddEdges::cookMySop(OP_Context &context)
{
	typedef dgal::simple_mesh<Imath::V3f>					simple_mesh;
	typedef dgal::EdgeIntersection<unsigned int, float> 	edge_intersection;
	typedef std::pair<unsigned int, unsigned int>			edge_type;

	hdk_utils::ScopedCook scc(*this, context, "Performing edges add");
	if(!scc.good())
		return error();

	double now = context.getTime();

	// inputs
	const GU_Detail* gdp0 = inputGeo(0);
	if (!gdp0 ) {
		SOP_ADD_FATAL(SOP_MESSAGE, "Not enough sources specified.");
	}

	unsigned int npoints0 = gdp0->points().entries();

	UT_String edgesAttrib;
	evalString(edgesAttrib, "edges_string", 0, now);
	std::string edgesAttribStr = edgesAttrib.toStdString();

	std::vector<std::string> toks;
	pystring::split(edgesAttribStr, toks);
	if(toks.empty())
	{
		duplicateSource(0, context);
		return error();
	}

	simple_mesh m, m2;
	simple_mesh* pm = NULL;
	std::vector<int> points_remap;
	std::vector<int> polys_remap;
	dgal::MeshRemapSettings<int> remap_settings(true, true, true, 0, 0, &points_remap, &polys_remap);
	std::map<std::string, unsigned int> new_points;

	// create new points
	{
		std::vector<edge_intersection> edgeints;

		std::string s = pystring::replace(edgesAttribStr, ",", " ");
		std::vector<std::string> toks;
		pystring::split(s, toks);

		for(unsigned int i=0; i<toks.size(); ++i)
		{
			// new point will be in form 'X-Y:f'
			if(toks[i].find('-') != std::string::npos)
			{
				s = pystring::replace(toks[i], "-", " ");
				s = pystring::replace(s, ":", " ");

				std::istringstream strm(s);
				edge_intersection ei;
				try {
					strm >> ei.m_point1 >> ei.m_point2 >> ei.m_u;
				}
				catch(const std::exception&) {
					continue;
				}

				new_points[toks[i]] = npoints0 + edgeints.size();
				edgeints.push_back(ei);
			}
		}

		if(new_points.empty())
		{
			// no new points means no point remapping
			remap_settings.m_genPointRemapping = false;
			remap_settings.m_identity_point_mapping = true;
		}
		else
		{
			dgal::addMeshPoints<GEO_Detail, std::vector<edge_intersection>::const_iterator>(
				*gdp0, m, edgeints.begin(), edgeints.end(), &points_remap, &polys_remap);
			pm = &m;
		}
	}
OP_ERROR SOP_SceneCacheSource::cookMySop( OP_Context &context )
{
	// make sure the state is valid
	if ( boost::indeterminate( m_static ) )
	{
		sceneChanged();
	}
	
	flags().setTimeDep( bool( !m_static ) );
	
	std::string file;
	if ( !ensureFile( file ) )
	{
		addError( SOP_ATTRIBUTE_INVALID, ( file + " is not a valid .scc" ).c_str() );
		gdp->clearAndDestroy();
		return error();
	}
	
	std::string path = getPath();
	Space space = getSpace();
	GeometryType geometryType = (GeometryType)this->evalInt( pGeometryType.getToken(), 0, 0 );
	
	UT_String tagFilterStr;
	getTagFilter( tagFilterStr );
	UT_StringMMPattern tagFilter;
	tagFilter.compile( tagFilterStr );
	
	UT_String shapeFilterStr;
	getShapeFilter( shapeFilterStr );
	UT_StringMMPattern shapeFilter;
	shapeFilter.compile( shapeFilterStr );
	
	UT_String p( "P" );
	UT_String attributeFilter;
	getAttributeFilter( attributeFilter );
	if ( !p.match( attributeFilter ) )
	{
		attributeFilter += " P";
	}
	
	UT_String attributeCopy;
	getAttributeCopy( attributeCopy );
	
	UT_String fullPathName;
	getFullPathName( fullPathName );
	
	ConstSceneInterfacePtr scene = this->scene( file, path );
	if ( !scene )
	{
		addError( SOP_ATTRIBUTE_INVALID, ( path + " is not a valid location in " + file ).c_str() );
		gdp->clearAndDestroy();
		return error();
	}
	
	MurmurHash hash;
	hash.append( file );
	hash.append( path );
	hash.append( space );
	hash.append( tagFilterStr );
	hash.append( shapeFilterStr );
	hash.append( attributeFilter );
	hash.append( attributeCopy );
	hash.append( fullPathName );
	hash.append( geometryType );
	hash.append( getObjectOnly() );
	
	if ( !m_loaded || m_hash != hash )
	{
		gdp->clearAndDestroy();
	}
	
	double readTime = time( context );
	Imath::M44d transform = ( space == World ) ? worldTransform( file, path, readTime ) : Imath::M44d();
	
	SceneInterface::Path rootPath;
	scene->path( rootPath );
	
	UT_Interrupt *progress = UTgetInterrupt();
	if ( !progress->opStart( ( "Cooking objects for " + getPath() ).c_str() ) )
	{
		addError( SOP_ATTRIBUTE_INVALID, "Cooking interrupted before it started" );
		gdp->clearAndDestroy();
		return error();
	}
	
	Parameters params;
	UT_String attribFilter;
	getAttributeFilter( attribFilter );
	params.attributeFilter = attribFilter.toStdString();
	params.attributeCopy = attributeCopy.toStdString();
	params.fullPathName = fullPathName.toStdString();
	params.geometryType = getGeometryType();
	getShapeFilter( params.shapeFilter );
	getTagFilter( params.tagFilter );
	
	// Building a map from shape name to primitive range, which will be used during
	// convertObject() to do a lazy update of animated primvars where possible, and
	// to destroy changing topology shapes when necessary.
	GA_ROAttributeRef nameAttrRef = gdp->findStringTuple( GA_ATTRIB_PRIMITIVE, "name" );
	if ( nameAttrRef.isValid() )
	{
		const GA_Attribute *attr = nameAttrRef.getAttribute();
		const GA_AIFSharedStringTuple *tuple = attr->getAIFSharedStringTuple();
		
		std::map<std::string, GA_OffsetList> offsets;
		GA_Range primRange = gdp->getPrimitiveRange();
		for ( GA_Iterator it = primRange.begin(); !it.atEnd(); ++it )
		{
			std::string current = "";
			if ( const char *value = tuple->getString( attr, it.getOffset() ) )
			{
				current = value;
			}
			
			std::map<std::string, GA_OffsetList>::iterator oIt = offsets.find( current );
			if ( oIt == offsets.end() )
			{
				oIt = offsets.insert( std::pair<std::string, GA_OffsetList>( current, GA_OffsetList() ) ).first;
			}
			
			oIt->second.append( it.getOffset() );
		}
		
		for ( std::map<std::string, GA_OffsetList>::iterator oIt = offsets.begin(); oIt != offsets.end(); ++oIt )
		{
			params.namedRanges[oIt->first] = GA_Range( gdp->getPrimitiveMap(), oIt->second );
		}
	}
	
	loadObjects( scene.get(), transform, readTime, space, params, rootPath.size() );
	
	if ( progress->opInterrupt( 100 ) )
	{
		addError( SOP_ATTRIBUTE_INVALID, "Cooking interrupted" );
		gdp->clearAndDestroy();		
		m_loaded = false;
		m_hash = MurmurHash();
	}
	else
	{
		m_loaded = true;
		m_hash = hash;
	}
	
	progress->opEnd();
	
	return error();
}
Beispiel #9
0
std::string SceneCacheNode<BaseType>::getPath() const
{
	UT_String value;
	this->evalString( value, pRoot.getToken(), 0, 0 );
	return ( value == "" ) ? "/" : value.toStdString();
}
Beispiel #10
0
std::string SceneCacheNode<BaseType>::getFile() const
{
	UT_String value;
	this->evalString( value, pFile.getToken(), 0, 0 );
	return value.toStdString();
}
OP_ERROR SOP_InterpolatedCacheReader::cookMySop( OP_Context &context )
{
	flags().setTimeDep( true );
	
	if ( lockInputs( context ) >= UT_ERROR_ABORT )
	{
		return error();
	}
	
	gdp->stashAll();
	
	float time = context.getTime();
	float frame = context.getFloatFrame();
	
	UT_String paramVal;
	
	evalString( paramVal, "cacheSequence", 0, time );
	std::string cacheFileName = paramVal.toStdString();
	
	evalString( paramVal, "objectFixes", 0, time );
	std::string objectPrefix = paramVal.toStdString();
	evalString( paramVal, "objectFixes", 1, time );
	std::string objectSuffix = paramVal.toStdString();
	
	evalString( paramVal, "attributeFixes", 0, time );
	std::string attributePrefix = paramVal.toStdString();
	evalString( paramVal, "attributeFixes", 1, time );
	std::string attributeSuffix = paramVal.toStdString();
	
	evalString( paramVal, "transformAttribute", 0, time );
	std::string transformAttribute = paramVal.toStdString();
	
	int samplesPerFrame = evalInt( "samplesPerFrame", 0, time );
	InterpolatedCache::Interpolation interpolation = (InterpolatedCache::Interpolation)evalInt( "interpolation", 0, time );
	GroupingMode groupingMode = (GroupingMode)evalInt( "groupingMode", 0, time );
	
	// create the InterpolatedCache
	if ( cacheFileName.compare( m_cacheFileName ) != 0 || samplesPerFrame != m_samplesPerFrame || interpolation != m_interpolation )
	{
		try
		{
			float fps = OPgetDirector()->getChannelManager()->getSamplesPerSec();
			OversamplesCalculator calc( fps, samplesPerFrame );
			m_cache = new InterpolatedCache( cacheFileName, interpolation, calc );
		}
		catch ( IECore::InvalidArgumentException e )
		{
			addWarning( SOP_ATTRIBUTE_INVALID, e.what() );
			unlockInputs();
			return error();
		}
		
		m_cacheFileName = cacheFileName;
		m_samplesPerFrame = samplesPerFrame;
		m_interpolation = interpolation;
	}
	
	if ( !m_cache )
	{
		addWarning( SOP_MESSAGE, "SOP_InterpolatedCacheReader: Cache Sequence not found" );
		unlockInputs();
		return error();
	}
	
	std::vector<InterpolatedCache::ObjectHandle> objects;
	std::vector<InterpolatedCache::AttributeHandle> attrs;
	
	try
	{
		m_cache->objects( frame, objects );
	}
	catch ( IECore::Exception e )
	{
		addWarning( SOP_ATTRIBUTE_INVALID, e.what() );
		unlockInputs();
		return error();
	}
	
	duplicatePointSource( 0, context );
	
	GA_ElementGroupTable *groups = 0;
	if ( groupingMode == PointGroup )
	{
		groups = &gdp->pointGroups();
	}
	else if ( groupingMode == PrimitiveGroup )
	{
		groups = &gdp->primitiveGroups();
	}
	
	for ( GA_GroupTable::iterator<GA_ElementGroup> it=groups->beginTraverse(); !it.atEnd(); ++it )
	{
		GA_ElementGroup *group = it.group();
		if ( group->getInternal() || group->isEmpty() )
		{
			continue;
		}
		
		// match GA_ElementGroup name to InterpolatedCache::ObjectHandle
		std::string searchName = objectPrefix + group->getName().toStdString() + objectSuffix;
		std::vector<InterpolatedCache::ObjectHandle>::iterator oIt = find( objects.begin(), objects.end(), searchName );
		if ( oIt == objects.end() )
		{
			continue;
		}
		
		CompoundObjectPtr attributes = 0;
		
		try
		{
			m_cache->attributes( frame, *oIt, attrs );
			attributes = m_cache->read( frame, *oIt );
		}
		catch ( IECore::Exception e )
		{
			addError( SOP_ATTRIBUTE_INVALID, e.what() );
			unlockInputs();
			return error();
		}
		
		const CompoundObject::ObjectMap &attributeMap = attributes->members();
		
		GA_Range pointRange;
		GA_Range primRange;
		GA_Range vertexRange;
		
		if ( groupingMode == PointGroup )
		{
			pointRange = gdp->getPointRange( (GA_PointGroup*)it.group() );
		}
		else if ( groupingMode == PrimitiveGroup )
		{
			primRange = gdp->getPrimitiveRange( (GA_PrimitiveGroup*)it.group() );
			const GA_PrimitiveList &primitives = gdp->getPrimitiveList();
			
			GA_OffsetList pointOffsets;
			GA_OffsetList vertOffsets;
			for ( GA_Iterator it=primRange.begin(); !it.atEnd(); ++it )
			{
				const GA_Primitive *prim = primitives.get( it.getOffset() );
				GA_Range primPointRange = prim->getPointRange();
				for ( GA_Iterator pIt=primPointRange.begin(); !pIt.atEnd(); ++pIt )
				{
					pointOffsets.append( pIt.getOffset() );
				}
				
				size_t numPrimVerts = prim->getVertexCount();
				for ( size_t v=0; v < numPrimVerts; v++ )
				{
					if ( prim->getTypeId() == GEO_PRIMPOLY )
					{
						vertOffsets.append( prim->getVertexOffset( numPrimVerts - 1 - v ) );
					}
					else
					{
						vertOffsets.append( prim->getVertexOffset( v ) );
					}
				}
			}
			
			pointOffsets.sortAndRemoveDuplicates();
			pointRange = GA_Range( gdp->getPointMap(), pointOffsets );
			vertexRange = GA_Range( gdp->getVertexMap(), vertOffsets );
		}
		
		// transfer the InterpolatedCache::Attributes onto the GA_ElementGroup
		for ( CompoundObject::ObjectMap::const_iterator aIt=attributeMap.begin(); aIt != attributeMap.end(); aIt++ )
		{
			Data *data = IECore::runTimeCast<Data>( aIt->second );
			if ( !data )
			{
				continue;
			}
			
			ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( data );
			if ( !converter )
 			{
 				continue;
 			}
			
			// strip the prefix/suffix from the GA_Attribute name
			std::string attrName = aIt->first.value();
			size_t prefixLength = attributePrefix.length();
			if ( prefixLength && ( search( attrName.begin(), attrName.begin()+prefixLength, attributePrefix.begin(), attributePrefix.end() ) == attrName.begin() ) )
			{
				attrName.erase( attrName.begin(), attrName.begin() + prefixLength );
			}
			
			size_t suffixLength = attributeSuffix.length();
			if ( suffixLength && ( search( attrName.end() - suffixLength, attrName.end(), attributeSuffix.begin(), attributeSuffix.end() ) == ( attrName.end() - suffixLength ) ) )
			{
				attrName.erase( attrName.end() - suffixLength, attrName.end() );
			}
			
			if ( attrName == "P" )
			{
				const V3fVectorData *positions = IECore::runTimeCast<const V3fVectorData>( data );
				if ( !positions )
				{
					continue;
				}
				
				size_t index = 0;
				size_t entries = pointRange.getEntries();
				const std::vector<Imath::V3f> &pos = positions->readable();
				
				// Attempting to account for the vertex difference between an IECore::CurvesPrimitive and Houdini curves.
				// As Houdini implicitly triples the endpoints of a curve, a cache generated from a single CurvesPrimitive
				// will have exactly four extra vertices. In this case, we adjust the cache by ignoring the first two and
				// last two V3fs. In all other cases, we report a warning and don't apply the cache to these points.
				if ( pos.size() - 4 == entries )
				{
					index = 2;
				}
				else if ( pos.size() != entries )
				{
					addWarning( SOP_ATTRIBUTE_INVALID, ( boost::format( "Geometry/Cache mismatch: %s contains %d points, while cache expects %d values for P." ) % group->getName().toStdString() % entries % pos.size() ).str().c_str() );
					continue;
				}
				
				/// \todo: try multi-threading this with a GA_SplittableRange
				for ( GA_Iterator it=pointRange.begin(); !it.atEnd(); ++it, ++index )
				{
					gdp->setPos3( it.getOffset(), IECore::convert<UT_Vector3>( pos[index] ) );
				}

			}
			else if ( groupingMode == PrimitiveGroup )
			{
				GA_Range currentRange;
				unsigned size = despatchTypedData<TypedDataSize, TypeTraits::IsVectorTypedData, DespatchTypedDataIgnoreError>( data );
				
				// check for existing attributes
				if ( gdp->findPrimitiveAttribute( attrName.c_str() ).isValid() && size == primRange.getEntries() )
				{
					currentRange = primRange;
				}
				else if ( gdp->findPointAttribute( attrName.c_str() ).isValid() && size == pointRange.getEntries() )
				{
					currentRange = pointRange;
				}
				else if ( gdp->findVertexAttribute( attrName.c_str() ).isValid() && size == vertexRange.getEntries() )
				{
					currentRange = vertexRange;
				}
				// fall back to Cortex standard inferred order
				else if ( size == primRange.getEntries() )
				{
					currentRange = primRange;
				}
				else if ( size == pointRange.getEntries() )
				{
					currentRange = pointRange;
				}
				else if ( size == vertexRange.getEntries() )
				{
					currentRange = vertexRange;
				}
				else
				{
					addWarning( SOP_ATTRIBUTE_INVALID, ( boost::format( "Geometry/Cache mismatch: %s: cache expects %d values for %s." ) % group->getName().toStdString() % size % attrName ).str().c_str() );
					continue;
				}
				
				converter->convert( attrName, gdp, currentRange );
			}
			else
			{
				converter->convert( attrName, gdp, pointRange );
			}
		}
		
		// if transformAttribute is specified, use to to transform the points
		if ( transformAttribute != "" )
		{
			const TransformationMatrixdData *transform = attributes->member<TransformationMatrixdData>( transformAttribute );
			if ( transform )
			{
				UT_Matrix4 matrix( IECore::convert<UT_Matrix4T<double> >( transform->readable().transform() ) );
				gdp->transformGroup( matrix, *group );
			}
			else
			{
				const TransformationMatrixfData *transform = attributes->member<TransformationMatrixfData>( transformAttribute );
				if ( transform )
				{
					UT_Matrix4 matrix = IECore::convert<UT_Matrix4>( transform->readable().transform() );
					gdp->transformGroup( matrix, *group );
				}
			}
		}
	}
	
	unlockInputs();
	return error();
}