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();
}
void SOP_SceneCacheSource::getNodeSpecificInfoText( OP_Context &context, OP_NodeInfoParms &parms )
{
	SceneCacheNode<SOP_Node>::getNodeSpecificInfoText( context, parms );
	
	// add type descriptions for the Cortex Objects
	GeometryType geometryType = (GeometryType)this->evalInt( pGeometryType.getToken(), 0, 0 );
	if ( geometryType == Cortex )
	{
		GEO_CortexPrimitive::infoText( getCookedGeo( context ), context, parms );
		return;
	}
	
	// add conversion details for Houdini geo
	UT_String p( "P" );
	UT_String filter;
	evalString( filter, pAttributeFilter.getToken(), 0, 0 );
	if ( !p.match( filter ) )
	{
		filter += " P";
	}
	UT_StringMMPattern attributeFilter;
	attributeFilter.compile( filter );
	
	/// \todo: this text could come from a static method on a class that manages these name relations (once that exists)
	parms.append( "Converting standard Cortex PrimitiveVariables:\n" );
	if ( UT_String( "s" ).multiMatch( attributeFilter ) && UT_String( "t" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  s,t -> uv\n" );
	}
	
	if ( UT_String( "Cs" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  Cs -> Cd\n" );
	}
	
	if ( UT_String( "Pref" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  Pref -> rest\n" );
	}
	
	if ( UT_String( "width" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  width -> pscale\n" );
	}
	
	if ( UT_String( "Os" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  Os -> Alpha\n" );
	}
}
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();
}
void SOP_CortexConverter::getNodeSpecificInfoText( OP_Context &context, OP_NodeInfoParms &parms )
{
	SOP_Node::getNodeSpecificInfoText( context, parms );
	
	GEO_CortexPrimitive::infoText( getCookedGeo( context ), context, parms );
	
	if ( !evalInt( pConvertStandardAttributes.getToken(), 0, 0 ) )
	{
		return;
	}
	
	UT_String p( "P" );
	UT_String filter;
	evalString( filter, pAttributeFilter.getToken(), 0, 0 );
	if ( !p.match( filter ) )
	{
		filter += " P";
	}
	UT_StringMMPattern attributeFilter;
	attributeFilter.compile( filter );
	
	/// \todo: this text could come from a static method on a class that manages these name relations (once that exists)
	parms.append( "Converting standard Cortex PrimitiveVariables:\n" );
	if ( UT_String( "s" ).multiMatch( attributeFilter ) && UT_String( "t" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  s,t <--> uv\n" );
	}
	
	if ( UT_String( "Cs" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  Cs <--> Cd\n" );
	}
	
	if ( UT_String( "Pref" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  Pref <--> rest\n" );
	}
	
	if ( UT_String( "width" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  width <--> pscale\n" );
	}
	
	if ( UT_String( "Os" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  Os <--> Alpha\n" );
	}
}
void SOP_SceneCacheSource::getNodeSpecificInfoText( OP_Context &context, OP_NodeInfoParms &parms )
{
	SceneCacheNode<SOP_Node>::getNodeSpecificInfoText( context, parms );
	
	UT_String p( "P" );
	UT_String filter;
	evalString( filter, pAttributeFilter.getToken(), 0, 0 );
	if ( !p.match( filter ) )
	{
		filter += " P";
	}
	UT_StringMMPattern attributeFilter;
	attributeFilter.compile( filter );
	
	/// \todo: this text could come from a static method on a class that manages these name relations (once that exists)
	parms.append( "Converting standard Cortex PrimitiveVariables:\n" );
	if ( UT_String( "s" ).multiMatch( attributeFilter ) && UT_String( "t" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  s,t -> uv\n" );
	}
	
	if ( UT_String( "Cs" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  Cs -> Cd\n" );
	}
	
	if ( UT_String( "Pref" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  Pref -> rest\n" );
	}
	
	if ( UT_String( "width" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  width -> pscale\n" );
	}
	
	if ( UT_String( "Os" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  Os -> Alpha\n" );
	}
}
void SOP_SceneCacheSource::getNodeSpecificInfoText( OP_Context &context, OP_NodeInfoParms &parms )
{
	SceneCacheNode<SOP_Node>::getNodeSpecificInfoText( context, parms );
	
	// add type descriptions for the Cortex Objects
	GeometryType geometryType = (GeometryType)this->evalInt( pGeometryType.getToken(), 0, 0 );
	if ( geometryType == Cortex )
	{
		std::map<std::string, int> typeMap;
		const GU_Detail *geo = getCookedGeo( context );
		if ( !geo )
		{
			return;
		}
		
		const GA_PrimitiveList &primitives = geo->getPrimitiveList();
		for ( GA_Iterator it=geo->getPrimitiveRange().begin(); !it.atEnd(); ++it )
		{
			const GA_Primitive *prim = primitives.get( it.getOffset() );
			if ( prim->getTypeId() == GU_CortexPrimitive::typeId() )
			{
				const IECore::Object *object = ((GU_CortexPrimitive *)prim)->getObject();
				if ( object )
				{
					typeMap[object->typeName()] += 1;
				}
			}
		}
		
		if ( typeMap.empty() )
		{
			return;
		}
		
		parms.append( "Cortex Object Details:\n" );
		for ( std::map<std::string, int>::iterator it = typeMap.begin(); it != typeMap.end(); ++it )
		{
			parms.append( ( boost::format( "  %d " + it->first + "s\n" ) % it->second ).str().c_str() );
		}
		
		return;
	}
	
	// add conversion details for Houdini geo
	UT_String p( "P" );
	UT_String filter;
	evalString( filter, pAttributeFilter.getToken(), 0, 0 );
	if ( !p.match( filter ) )
	{
		filter += " P";
	}
	UT_StringMMPattern attributeFilter;
	attributeFilter.compile( filter );
	
	/// \todo: this text could come from a static method on a class that manages these name relations (once that exists)
	parms.append( "Converting standard Cortex PrimitiveVariables:\n" );
	if ( UT_String( "s" ).multiMatch( attributeFilter ) && UT_String( "t" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  s,t -> uv\n" );
	}
	
	if ( UT_String( "Cs" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  Cs -> Cd\n" );
	}
	
	if ( UT_String( "Pref" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  Pref -> rest\n" );
	}
	
	if ( UT_String( "width" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  width -> pscale\n" );
	}
	
	if ( UT_String( "Os" ).multiMatch( attributeFilter ) )
	{
		parms.append( "  Os -> Alpha\n" );
	}
}
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_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();
}
bool SOP_SceneCacheSource::convertObject( const IECore::Object *object, const std::string &name, const SceneInterface *scene, Parameters &params )
{
	ToHoudiniGeometryConverterPtr converter = nullptr;
	if ( params.geometryType == Cortex )
	{
		converter = new ToHoudiniCortexObjectConverter( object );
	}
	else
	{
		const VisibleRenderable *renderable = IECore::runTimeCast<const VisibleRenderable>( object );
		if ( !renderable )
		{
			return false;
		}

		converter = ToHoudiniGeometryConverter::create( renderable );
	}

	if ( !converter )
	{
		return false;
	}

	// we need to set the name regardless of whether
	// we're reusing prims or doing the full conversion
	// because this parameter can have an affect in
	// transferAttribs() as well as convert()
	converter->nameParameter()->setTypedValue( name );

	// check the primitve range map to see if this shape exists already
	std::map<std::string, GA_Range>::iterator rIt = params.namedRanges.find( name );
	if ( rIt != params.namedRanges.end() && !rIt->second.isEmpty() )
	{
		GA_Range primRange = rIt->second;
		const Primitive *primitive = IECore::runTimeCast<const Primitive>( object );
		if ( primitive && !params.hasAnimatedTopology && params.hasAnimatedPrimVars )
		{
			// this means constant topology and primitive variables, even though multiple samples were written
			if ( params.animatedPrimVars.empty() )
			{
				return true;
			}

			GA_Range pointRange( *gdp, primRange, GA_ATTRIB_POINT, GA_Range::primitiveref(), false );

			// update the animated primitive variables only
			std::string animatedPrimVarStr = "";
			for ( std::vector<InternedString>::const_iterator it = params.animatedPrimVars.begin(); it != params.animatedPrimVars.end(); ++it )
			{
				animatedPrimVarStr += it->string() + " ";
			}

			converter->attributeFilterParameter()->setTypedValue( animatedPrimVarStr );

			try
			{
				converter->transferAttribs( gdp, pointRange, primRange );
				return true;
			}
			catch ( std::exception &e )
			{
				addWarning( SOP_MESSAGE, e.what() );
				return false;
			}
			catch ( ... )
			{
				addWarning( SOP_MESSAGE, "Attribute transfer failed for unknown reasons" );
				return false;
			}
		}
		else
		{
			// topology is changing, so destroy the exisiting primitives
			gdp->destroyPrimitives( primRange, true );
		}
	}

	// fallback to full conversion
	converter->attributeFilterParameter()->setTypedValue( params.attributeFilter );

	try
	{
		GA_Offset firstNewPrim = gdp->getPrimitiveMap().lastOffset() + 1;

		bool status = converter->convert( myGdpHandle );

		// adds the full path in addition to the relative name
		const GA_IndexMap &primMap = gdp->getPrimitiveMap();
		GA_Range newPrims( primMap, firstNewPrim, primMap.lastOffset() + 1 );

		if ( params.fullPathName != "" )
		{
			if ( newPrims.isValid() )
			{
				std::string fullName;
				SceneInterface::Path path;
				scene->path( path );
				SceneInterface::pathToString( path, fullName );

				GA_RWAttributeRef pathAttribRef = ToHoudiniStringVectorAttribConverter::convertString( params.fullPathName, fullName, gdp, newPrims );
				status = status && pathAttribRef.isValid();
			}
		}

		if ( params.tagGroups )
		{
			static UT_StringMMPattern convertTagFilter;
			if( convertTagFilter.isEmpty() )
			{
				convertTagFilter.compile( "ObjectType:*" );
			}
			SceneInterface::NameList tags;
			scene->readTags( tags, IECoreScene::SceneInterface::LocalTag );
			for ( SceneInterface::NameList::const_iterator it=tags.begin(); it != tags.end(); ++it )
			{
				UT_String tag( *it );
				// skip this tag because it's used behind the scenes
				if ( tag.multiMatch( convertTagFilter ) )
				{
					continue;
				}

				// replace this special character found in SCC tags that will prevent the group from being created
				tag.substitute(":", "_");

				tag.prepend("ieTag_");

				GA_PrimitiveGroup *group = gdp->findPrimitiveGroup(tag);
				if ( !group )
				{
					group = gdp->newPrimitiveGroup(tag);
				}
				group->addRange(newPrims);
			}
		}

		return status;
	}
	catch ( std::exception &e )
	{
		addWarning( SOP_MESSAGE, e.what() );
		return false;
	}
	catch ( ... )
	{
		addWarning( SOP_MESSAGE, "Conversion failed for unknown reasons" );
		return false;
	}
}
Beispiel #10
0
void SceneCacheNode<BaseType>::getShapeFilter( UT_StringMMPattern &filter ) const
{
	UT_String value;
	getShapeFilter( value );
	filter.compile( value );
}
void ToHoudiniGeometryConverter::transferAttribValues(
	const Primitive *primitive, GU_Detail *geo,
	const GA_Range &points, const GA_Range &prims,
	PrimitiveVariable::Interpolation vertexInterpolation,
	PrimitiveVariable::Interpolation primitiveInterpolation,
	PrimitiveVariable::Interpolation pointInterpolation,
	PrimitiveVariable::Interpolation detailInterpolation
) const
{
	GA_OffsetList offsets;
	if ( prims.isValid() )
	{
		const GA_PrimitiveList &primitives = geo->getPrimitiveList();
		for ( GA_Iterator it=prims.begin(); !it.atEnd(); ++it )
		{
			const GA_Primitive *prim = primitives.get( it.getOffset() );
			size_t numPrimVerts = prim->getVertexCount();
			for ( size_t v=0; v < numPrimVerts; v++ )
			{
				if ( prim->getTypeId() == GEO_PRIMPOLY )
				{
					offsets.append( prim->getVertexOffset( numPrimVerts - 1 - v ) );
				}
				else
				{
					offsets.append( prim->getVertexOffset( v ) );
				}
			}
		}
	}

	GA_Range vertRange( geo->getVertexMap(), offsets );
	
	UT_String filter( attributeFilterParameter()->getTypedValue() );
	
	// match all the string variables to each associated indices variable
	/// \todo: replace all this logic with IECore::IndexedData once it exists...
	PrimitiveVariableMap stringsToIndices;
	for ( PrimitiveVariableMap::const_iterator it=primitive->variables.begin() ; it != primitive->variables.end(); it++ )
	{
		if ( !primitive->isPrimitiveVariableValid( it->second ) )
		{
			IECore::msg( IECore::MessageHandler::Warning, "ToHoudiniGeometryConverter", "PrimitiveVariable " + it->first + " is invalid. Ignoring." );
			filter += UT_String( " ^" + it->first );
			continue;
		}

		ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( it->second.data.get() );
		if ( !converter )
		{
			continue;
		}
		
		if ( it->second.data->isInstanceOf( StringVectorDataTypeId ) )
		{
			std::string indicesVariableName = it->first + "Indices";
			PrimitiveVariableMap::const_iterator indices = primitive->variables.find( indicesVariableName );
			if ( indices != primitive->variables.end() && indices->second.data->isInstanceOf( IntVectorDataTypeId ) && primitive->isPrimitiveVariableValid( indices->second ) )
			{
				stringsToIndices[it->first] = indices->second;
				filter += UT_String( " ^" + indicesVariableName );
			}
		}
	}
	
	bool convertStandardAttributes = m_convertStandardAttributesParameter->getTypedValue();
	if ( convertStandardAttributes && UT_String( "s" ).multiMatch( filter ) && UT_String( "t" ).multiMatch( filter ) )
	{
		// convert s and t to uv
		PrimitiveVariableMap::const_iterator sPrimVar = primitive->variables.find( "s" );
		PrimitiveVariableMap::const_iterator tPrimVar = primitive->variables.find( "t" );
		if ( sPrimVar != primitive->variables.end() && tPrimVar != primitive->variables.end() )
		{
			if ( sPrimVar->second.interpolation == tPrimVar->second.interpolation )
			{
				const FloatVectorData *sData = runTimeCast<const FloatVectorData>( sPrimVar->second.data.get() );
				const FloatVectorData *tData = runTimeCast<const FloatVectorData>( tPrimVar->second.data.get() );
				if ( sData && tData )
				{
					const std::vector<float> &s = sData->readable();
					const std::vector<float> &t = tData->readable();
					
					std::vector<Imath::V3f> uvw;
					uvw.reserve( s.size() );
					for ( size_t i=0; i < s.size(); ++i )
					{
						uvw.push_back( Imath::V3f( s[i], 1 - t[i], 0 ) );
					}
					
					GA_Range range = vertRange;
					if ( sPrimVar->second.interpolation == pointInterpolation )
					{
						range = points;
					}
					
					ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( new V3fVectorData( uvw ) );
					converter->convert( "uv", geo, range );
					filter += " ^s ^t";
				}
			}
		}
	}
	
 	UT_StringMMPattern attribFilter;
	attribFilter.compile( filter );
	
	// add the primitive variables to the various GEO_AttribDicts based on interpolation type
	for ( PrimitiveVariableMap::const_iterator it=primitive->variables.begin() ; it != primitive->variables.end(); it++ )
	{
		UT_String varName( it->first );
		if ( !varName.multiMatch( attribFilter ) )
		{
			continue;
		}
		
		PrimitiveVariable primVar = processPrimitiveVariable( primitive, it->second );
		ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( primVar.data.get() );
		if ( !converter )
		{
			continue;
		}
		
		PrimitiveVariable::Interpolation interpolation = primVar.interpolation;
		
		if ( converter->isInstanceOf( (IECore::TypeId)ToHoudiniStringVectorAttribConverterTypeId ) )
		{
			PrimitiveVariableMap::const_iterator indices = stringsToIndices.find( it->first );
			if ( indices != stringsToIndices.end() )
			{
				ToHoudiniStringVectorAttribConverter *stringVectorConverter = IECore::runTimeCast<ToHoudiniStringVectorAttribConverter>( converter.get() );
				PrimitiveVariable indicesPrimVar = processPrimitiveVariable( primitive, indices->second );
				stringVectorConverter->indicesParameter()->setValidatedValue( indicesPrimVar.data );
				interpolation = indices->second.interpolation;
			}
		}
		
		const std::string name = ( convertStandardAttributes ) ? processPrimitiveVariableName( it->first ) : it->first;
		
		if ( interpolation == detailInterpolation )
 		{
			// add detail attribs
			try
			{
				converter->convert( name, geo );
			}
			catch ( std::exception &e )
			{
				throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Detail Attrib: " + e.what() );
			}
	 	}
		else if ( interpolation == pointInterpolation )
		{
			// add point attribs
			if ( name == "P" )
			{
				// special case for P
				transferP( runTimeCast<const V3fVectorData>( primVar.data.get() ), geo, points );
			}
			else
			{
 				try
				{
					GA_RWAttributeRef attrRef = converter->convert( name, geo, points );
					
					// mark rest as non-transforming so it doesn't get manipulated once inside Houdini
					if ( name == "rest" || name == "Pref" )
					{
						attrRef.getAttribute()->setNonTransforming( true );
					}
				}
				catch ( std::exception &e )
				{
					throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Point Attrib: " + e.what() );
				}
			}
		}
		else if ( interpolation == primitiveInterpolation )
		{
			// add primitive attribs
			try
			{
				converter->convert( name, geo, prims );
			}
			catch ( std::exception &e )
			{
				throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Primitive Attrib: " + e.what() );
			}
		}
		else if ( interpolation == vertexInterpolation )
		{
			// add vertex attribs
			try
			{
				converter->convert( name, geo, vertRange );
			}
			catch ( std::exception &e )
			{
				throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Vertex Attrib: " + e.what() );
			}
		}
	}
	
	// backwards compatibility with older data
	const StringData *nameData = primitive->blindData()->member<StringData>( "name" );
	if ( nameData && prims.isValid() )
	{
		ToHoudiniStringVectorAttribConverter::convertString( "name", nameData->readable(), geo, prims );
	}
}
void ToHoudiniGeometryConverter::transferAttribValues(
	const Primitive *primitive, GU_Detail *geo,
	const GA_Range &points, const GA_Range &prims,
	PrimitiveVariable::Interpolation vertexInterpolation,
	PrimitiveVariable::Interpolation primitiveInterpolation,
	PrimitiveVariable::Interpolation pointInterpolation,
	PrimitiveVariable::Interpolation detailInterpolation
) const
{
	GA_OffsetList offsets;
	if ( prims.isValid() )
	{
		const GA_PrimitiveList &primitives = geo->getPrimitiveList();
		for ( GA_Iterator it=prims.begin(); !it.atEnd(); ++it )
		{
			const GA_Primitive *prim = primitives.get( it.getOffset() );
			size_t numPrimVerts = prim->getVertexCount();
			for ( size_t v=0; v < numPrimVerts; v++ )
			{
				if ( prim->getTypeId() == GEO_PRIMPOLY )
				{
					offsets.append( prim->getVertexOffset( numPrimVerts - 1 - v ) );
				}
				else
				{
					offsets.append( prim->getVertexOffset( v ) );
				}
			}
		}
	}

	GA_Range vertRange( geo->getVertexMap(), offsets );

	UT_String filter( attributeFilterParameter()->getTypedValue() );

	bool convertStandardAttributes = m_convertStandardAttributesParameter->getTypedValue();

	// process all primvars with UV interpretation
	for ( const auto &it : primitive->variables)
	{

		if ( !UT_String( it.first ).multiMatch( filter ) )
		{
			continue;
		}

		if (const V2fVectorData *uvData = runTimeCast<const V2fVectorData> ( it.second.data.get() ) )
		{
			if ( uvData->getInterpretation() != GeometricData::UV )
			{
				continue;
			}

			PrimitiveVariable::IndexedView<Imath::V2f> uvIndexedView ( it.second );

			// Houdini prefers a V3f uvw rather than V2f uv,
			// though they advise setting the 3rd component to 0.
			std::vector<Imath::V3f> uvw;
			uvw.reserve( uvIndexedView.size() );
			for ( size_t i=0; i < uvIndexedView.size(); ++i )
			{
				uvw.emplace_back( uvIndexedView[i][0], uvIndexedView[i][1], 0 );
			}

			GA_Range range = vertRange;
			if ( it.second.interpolation == pointInterpolation )
			{
				range = points;
			}

			V3fVectorData::Ptr uvwData = new V3fVectorData( uvw );
			uvwData->setInterpretation( GeometricData::UV );

			ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( uvwData.get() );
			converter->convert( it.first, geo, range );
			filter += " ^" + it.first;
		}
	}


 	UT_StringMMPattern attribFilter;
	attribFilter.compile( filter );

	// add the primitive variables to the various GEO_AttribDicts based on interpolation type
	for ( PrimitiveVariableMap::const_iterator it=primitive->variables.begin() ; it != primitive->variables.end(); it++ )
	{
		if( !primitive->isPrimitiveVariableValid( it->second ) )
		{
			IECore::msg( IECore::MessageHandler::Warning, "ToHoudiniGeometryConverter", "PrimitiveVariable " + it->first + " is invalid. Ignoring." );
			continue;
		}

		UT_String varName( it->first );
		if ( !varName.multiMatch( attribFilter ) )
		{
			continue;
		}

		PrimitiveVariable primVar = processPrimitiveVariable( primitive, it->second );

		DataPtr data = nullptr;
		ToHoudiniAttribConverterPtr converter = nullptr;

		if( primVar.indices && primVar.data->typeId() == StringVectorDataTypeId )
		{
			// we want to process the indexed strings rather than the expanded strings
			converter = ToHoudiniAttribConverter::create( primVar.data.get() );
			if( ToHoudiniStringVectorAttribConverter *stringVectorConverter = IECore::runTimeCast<ToHoudiniStringVectorAttribConverter>( converter.get() ) )
			{
				stringVectorConverter->indicesParameter()->setValidatedValue( primVar.indices.get() );
			}
		}
		else
		{
			// all other primitive variables must be expanded
			data = primVar.expandedData();
			converter = ToHoudiniAttribConverter::create( data.get() );
		}

		if ( !converter )
		{
			continue;
		}

		const std::string name = ( convertStandardAttributes ) ? processPrimitiveVariableName( it->first ) : it->first;

		if ( primVar.interpolation == detailInterpolation )
 		{
			// add detail attribs
			try
			{
				converter->convert( name, geo );
			}
			catch ( std::exception &e )
			{
				throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Detail Attrib: " + e.what() );
			}
	 	}
		else if ( primVar.interpolation == pointInterpolation )
		{

#if UT_MAJOR_VERSION_INT < 15

			// add point attribs
			if ( name == "P" )
			{
				// special case for P
				transferP( runTimeCast<const V3fVectorData>( primVar.data.get() ), geo, points );
			}
			else

#endif

			{
 				try
				{
					GA_RWAttributeRef attrRef = converter->convert( name, geo, points );

					// mark rest as non-transforming so it doesn't get manipulated once inside Houdini
					if ( name == "rest" || name == "Pref" )
					{

#if UT_MAJOR_VERSION_INT >= 15

						attrRef.setTypeInfo( GA_TYPE_VOID );

#else

						attrRef.getAttribute()->setNonTransforming( true );

#endif

					}
				}
				catch ( std::exception &e )
				{
					throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Point Attrib: " + e.what() );
				}
			}
		}
		else if ( primVar.interpolation == primitiveInterpolation )
		{
			// add primitive attribs
			try
			{
				converter->convert( name, geo, prims );
			}
			catch ( std::exception &e )
			{
				throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Primitive Attrib: " + e.what() );
			}
		}
		else if ( primVar.interpolation == vertexInterpolation )
		{
			// add vertex attribs
			try
			{
				converter->convert( name, geo, vertRange );
			}
			catch ( std::exception &e )
			{
				throw IECore::Exception( "PrimitiveVariable \"" + it->first + "\" could not be converted as a Vertex Attrib: " + e.what() );
			}
		}
	}

	// backwards compatibility with older data
	const StringData *nameData = primitive->blindData()->member<StringData>( "name" );
	if ( nameData && prims.isValid() )
	{
		ToHoudiniStringVectorAttribConverter::convertString( "name", nameData->readable(), geo, prims );
	}
}
void OBJ_SceneCacheTransform::pushToHierarchy()
{
	UT_String attribFilter, attribCopy, shapeFilter, fullPathName;
	bool tagGroups = getTagGroups();
	getAttributeFilter( attribFilter );
	getAttributeCopy( attribCopy );
	getShapeFilter( shapeFilter );
	getFullPathName( fullPathName );
	GeometryType geometryType = getGeometryType();

	UT_String tagFilterStr;
	UT_StringMMPattern tagFilter;
	getTagFilter( tagFilterStr );
	tagFilter.compile( tagFilterStr );

	UT_PtrArray<OP_Node*> children;
	int numSceneNodes = getOpsByName( OBJ_SceneCacheTransform::typeName, children );
	for ( int i=0; i < numSceneNodes; ++i )
	{
		OBJ_SceneCacheTransform *xform = reinterpret_cast<OBJ_SceneCacheTransform*>( children( i ) );
		xform->setAttributeFilter( attribFilter );
		xform->setAttributeCopy( attribCopy );
		xform->setShapeFilter( shapeFilter );
		xform->setFullPathName( fullPathName );
		xform->setGeometryType( geometryType );

		std::string file;
		bool visible = false;
		if ( IECoreScene::ConstSceneInterfacePtr scene = xform->scene() )
		{
			if ( tagged( scene.get(), tagFilter ) )
			{
				visible = true;
				xform->setTagFilter( tagFilterStr );
				xform->setTagGroups( tagGroups );
			}
		}

		xform->setRender( visible );
		xform->setDisplay( visible );
		xform->pushToHierarchy();
	}

	children.clear();
	numSceneNodes = getOpsByName( OBJ_SceneCacheGeometry::typeName, children );
	for ( int i=0; i < numSceneNodes; ++i )
	{
		OBJ_SceneCacheGeometry *geo = reinterpret_cast<OBJ_SceneCacheGeometry*>( children( i ) );
		geo->setAttributeFilter( attribFilter );
		geo->setAttributeCopy( attribCopy );
		geo->setShapeFilter( shapeFilter );
		geo->setFullPathName( fullPathName );
		geo->setGeometryType( (OBJ_SceneCacheGeometry::GeometryType)geometryType );

		std::string file;
		bool visible = false;
		if ( IECoreScene::ConstSceneInterfacePtr scene = geo->scene() )
		{
			visible = tagged( scene.get(), tagFilter );
			if ( visible )
			{
				geo->setTagFilter( tagFilterStr );
				geo->setTagGroups( tagGroups );
			}
		}

		geo->setRender( visible );
		geo->setDisplay( visible );
		geo->pushToHierarchy();
	}
}
void
GusdPrimWrapper::loadPrimvars( 
    UsdTimeCode               time,
    const GT_RefineParms*     rparms,
    int                       minUniform,
    int                       minPoint,
    int                       minVertex,
    const string&             primPath,
    GT_AttributeListHandle*   vertex,
    GT_AttributeListHandle*   point,
    GT_AttributeListHandle*   primitive,
    GT_AttributeListHandle*   constant,
    const GT_DataArrayHandle& remapIndicies ) const
{
    // Primvars will be loaded if they match a provided pattern.
    // By default, set the pattern to match only "Cd". Then write
    // over this pattern if there is one provided in rparms.
    const char* Cd = "Cd";
    UT_String primvarPatternStr(Cd);

    if (rparms) {
        rparms->import("usd:primvarPattern", primvarPatternStr);
    }

    UT_StringMMPattern primvarPattern;
    if (primvarPatternStr) {
        primvarPattern.compile(primvarPatternStr);
    }

    std::vector<UsdGeomPrimvar> authoredPrimvars;
    bool hasCdPrimvar = false;

    {
        UsdGeomImageable prim = getUsdPrim();

        UsdGeomPrimvar colorPrimvar = prim.GetPrimvar(GusdTokens->Cd);
        if (colorPrimvar && colorPrimvar.GetAttr().HasAuthoredValue()) {
            hasCdPrimvar = true;
        }

        // It's common for "Cd" to be the only primvar to load.
        // In this case, avoid getting all other authored primvars.
        if (primvarPatternStr == Cd) {
            if (hasCdPrimvar) {
                authoredPrimvars.push_back(colorPrimvar);
            } else {
                // There is no authored "Cd" primvar.
                // Try to find "displayColor" instead.
                colorPrimvar = prim.GetPrimvar(UsdGeomTokens->primvarsDisplayColor);
                if (colorPrimvar &&
                    colorPrimvar.GetAttr().HasAuthoredValue()) {
                    authoredPrimvars.push_back(colorPrimvar);
                }
            }
        } else if (!primvarPattern.isEmpty()) {
            authoredPrimvars = prim.GetAuthoredPrimvars();
        }
    }    

    // Is it better to sort the attributes and build the attributes all at once.

    for( const UsdGeomPrimvar &primvar : authoredPrimvars )
    {
        DBG(cerr << "loadPrimvar " << primvar.GetPrimvarName() << "\t" << primvar.GetTypeName() << "\t" << primvar.GetInterpolation() << endl);

        UT_String name(primvar.GetPrimvarName());

        // One special case we always handle here is to change
        // the name of the USD "displayColor" primvar to "Cd",
        // as long as there is not already a "Cd" primvar.
        if (!hasCdPrimvar && 
            primvar.GetName() == UsdGeomTokens->primvarsDisplayColor) {
            name = Cd;
        }

        // If the name of this primvar doesn't
        // match the primvarPattern, skip it.
        if (!name.multiMatch(primvarPattern)) {
            continue;
        }

        GT_DataArrayHandle gtData = convertPrimvarData( primvar, time );

        if( !gtData )
        {
            TF_WARN( "Failed to convert primvar %s:%s %s.", 
                        primPath.c_str(),
                        primvar.GetPrimvarName().GetText(),
                        primvar.GetTypeName().GetAsToken().GetText() );
            continue;
        }

#if SYS_VERSION_FULL_INT >= 0x11050000
        // Encode the USD primvar names into something safe for the Houdini
        // geometry attribute name. This allows round tripping of namespaced
        // primvars from USD -> Houdini -> USD.
        UT_StringHolder attrname = UT_VarEncode::encode(name);
#else
        UT_StringHolder attrname = name;
#endif

        // usd vertex primvars are assigned to points
        if( primvar.GetInterpolation() == UsdGeomTokens->vertex )
        {
            if( gtData->entries() < minPoint ) {
                TF_WARN( "Not enough values found for primvar: %s:%s. "
                         "%zd values given for %d points.",
                         primPath.c_str(),
                         primvar.GetPrimvarName().GetText(),
                         gtData->entries(), minPoint );
            }
            else {
                if (remapIndicies) {
                    gtData = new GT_DAIndirect( remapIndicies, gtData );
                }
                if( point ) {
                    *point = (*point)->addAttribute( attrname.c_str(), gtData, true );
                }
            }
        }
        else if( primvar.GetInterpolation() == UsdGeomTokens->faceVarying )
        {
            if( gtData->entries() < minVertex ) {
                TF_WARN( "Not enough values found for primvar: %s:%s. "
                         "%zd values given for %d verticies.", 
                         primPath.c_str(),
                         primvar.GetPrimvarName().GetText(), 
                         gtData->entries(), minVertex );
            }
            else if( vertex ) {           
                *vertex = (*vertex)->addAttribute( attrname.c_str(), gtData, true );
            }
        }
        else if( primvar.GetInterpolation() == UsdGeomTokens->uniform )
        {
            if( gtData->entries() < minUniform ) {
                TF_WARN( "Not enough values found for primvar: %s:%s. "
                         "%zd values given for %d faces.", 
                         primPath.c_str(),
                         primvar.GetPrimvarName().GetText(),
                         gtData->entries(), minUniform );
            }
            else if( primitive ) {
                *primitive = (*primitive)->addAttribute( attrname.c_str(), gtData, true );
            }
        }
        else if( primvar.GetInterpolation() == UsdGeomTokens->constant )
        {
            if( constant ) {
                *constant = (*constant)->addAttribute( attrname.c_str(), gtData, true );
            }
        }
    }
}