bool SOP_SceneCacheSource::convertObject( const IECore::Object *object, const std::string &name, const std::string &attributeFilter, GeometryType geometryType, bool hasAnimatedTopology, bool hasAnimatedPrimVars, const std::vector<InternedString> &animatedPrimVars )
{
	ToHoudiniGeometryConverterPtr converter = 0;
	if ( 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;
	}
	
	// attempt to optimize the conversion by re-using animated primitive variables
	const Primitive *primitive = IECore::runTimeCast<const Primitive>( object );
	GA_ROAttributeRef nameAttrRef = gdp->findStringTuple( GA_ATTRIB_PRIMITIVE, "name" );
	GA_Range primRange = gdp->getRangeByValue( nameAttrRef, name.c_str() );
	if ( primitive && !hasAnimatedTopology && hasAnimatedPrimVars && nameAttrRef.isValid() && !primRange.isEmpty() )
	{
		// this means constant topology and primitive variables, even though multiple samples were written
		if ( animatedPrimVars.empty() )
		{
			return true;
		}
		
		GA_Range pointRange( *gdp, primRange, GA_ATTRIB_POINT, GA_Range::primitiveref(), false );
		
		std::string animatedPrimVarStr = "";
		for ( std::vector<InternedString>::const_iterator it = animatedPrimVars.begin(); it != animatedPrimVars.end(); ++it )
		{
			animatedPrimVarStr += it->string() + " ";
		}
		
		converter->attributeFilterParameter()->setTypedValue( animatedPrimVarStr );
		converter->transferAttribs( gdp, pointRange, primRange );
		
		return true;
	}
	else
	{
		gdp->destroyPrimitives( primRange, true );
	}
	
	// fallback to full conversion
	converter->nameParameter()->setTypedValue( name );
	converter->attributeFilterParameter()->setTypedValue( attributeFilter );
	return converter->convert( myGdpHandle );
}
void SOP_CortexConverter::doConvert( const GU_DetailHandle &handle, const std::string &name, ResultType type, const std::string &attributeFilter, bool convertStandardAttributes )
{
	if ( handle.isNull() )
	{
		addError( SOP_MESSAGE, ( "Could not extract the geometry named " + name ).c_str() );
		return;
	}
	
	FromHoudiniGeometryConverterPtr fromConverter = FromHoudiniGeometryConverter::create( handle );
	if ( !fromConverter )
	{
		addError( SOP_MESSAGE, ( "Could not convert the geometry named " + name ).c_str() );
		return;
	}
	
	IECore::ObjectPtr result = fromConverter->convert();
	if ( !result )
	{
		addError( SOP_MESSAGE, ( "Could not find Cortex Object named " + name + " on input geometry" ).c_str() );
		return;
	}
	
	if ( IECore::ParameterisedProcedural *procedural = IECore::runTimeCast<IECore::ParameterisedProcedural>( result.get() ) )
	{
		IECore::CapturingRendererPtr renderer = new IECore::CapturingRenderer();
		// We are acquiring and releasing the GIL here to ensure that it is released when we render. This has
		// to be done because a procedural might jump between c++ and python a few times (i.e. if it spawns
		// subprocedurals that are implemented in python). In a normal call to cookMySop, this wouldn't be an
		// issue, but if cookMySop was called from HOM, hou.Node.cook appears to be holding onto the GIL.
		IECorePython::ScopedGILLock gilLock;
		{
			IECorePython::ScopedGILRelease gilRelease;
			{
				IECore::WorldBlock worldBlock( renderer );
				procedural->render( renderer.get() );
			}
		}
		
		result = boost::const_pointer_cast<IECore::Object>( IECore::runTimeCast<const IECore::Object>( renderer->world() ) );
	}
	
	ToHoudiniGeometryConverterPtr converter = ( type == Cortex ) ? new ToHoudiniCortexObjectConverter( result.get() ) : ToHoudiniGeometryConverter::create( result.get() );
	converter->nameParameter()->setTypedValue( name );
	converter->attributeFilterParameter()->setTypedValue( attributeFilter );
	converter->convertStandardAttributesParameter()->setTypedValue( convertStandardAttributes );
	
	if ( !converter->convert( myGdpHandle ) )
	{
		addError( SOP_MESSAGE, ( "Could not convert the Cortex Object named " + name + " to Houdini geometry" ).c_str() );
	}
}
GA_Detail::IOStatus GEO_CobIOTranslator::fileLoad( GEO_Detail *geo, UT_IStream &is, int ate_magic )
{
	((UT_IFStream&)is).close();
	
	ConstVisibleRenderablePtr renderable = 0;
	try
	{
		ReaderPtr reader = Reader::create( is.getLabel() );
		if ( reader->isInstanceOf( ParticleReaderTypeId ) )
		{
			reader->parameters()->parameter<IntParameter>( "realType" )->setNumericValue( ParticleReader::Float );
		}
		
		renderable = runTimeCast<VisibleRenderable>( reader->read() );
	}
	catch ( IECore::Exception e )
	{
		return false;
	}
	
	if ( !renderable )
	{
		return false;
	}
	
	ToHoudiniGeometryConverterPtr converter = ToHoudiniGeometryConverter::create( renderable );
	if ( !converter )
	{
		return false;
	}
	
	GU_DetailHandle handle;
	handle.allocateAndSet( (GU_Detail*)geo, false );
	
	return converter->convert( handle );
}
GA_Detail::IOStatus GEO_CobIOTranslator::fileLoad( GEO_Detail *geo, UT_IStream &is, int ate_magic )
{
	((UT_IFStream&)is).close();
	
	ConstObjectPtr object = 0;
	try
	{
		ReaderPtr reader = Reader::create( is.getLabel() );
		if ( !reader )
		{
			return false;
		}
		
		object = reader->read();
	}
	catch ( IECore::Exception e )
	{
		return false;
	}
	
	if ( !object )
	{
		return false;
	}
	
	ToHoudiniGeometryConverterPtr converter = ToHoudiniGeometryConverter::create( object.get() );
	if ( !converter )
	{
		return false;
	}
	
	GU_DetailHandle handle;
	handle.allocateAndSet( (GU_Detail*)geo, false );
	
	return converter->convert( handle );
}
void IECoreMantra::ProceduralPrimitive::addVisibleRenderable( VisibleRenderablePtr renderable )
{
	ToHoudiniGeometryConverterPtr converter = ToHoudiniGeometryConverter::create( renderable );
	if( !converter ) 
	{
		msg( Msg::Warning, "ProceduralPrimitive::addVisibleRenderable", "converter could not be found" );
		return;
	}
	GU_Detail *gdp = allocateGeometry();
	GU_DetailHandle handle;
	handle.allocateAndSet( (GU_Detail*)gdp, false );
	bool converted = converter->convert( handle );
	if ( !converted )
	{
		msg( Msg::Warning, "ProceduralPrimitive::addVisibleRenderable", "converter failed" );
		return;
	}
	/// \todo ToHoudiniGeometryConverter does not create a Houdini style uv attribute.
	/// We make one from s and t. This code should probably live in a converter or in an Op that
	/// remaps IECore conventions to common Houdini ones.
	MeshPrimitivePtr mesh = runTimeCast<MeshPrimitive> (renderable);
	if ( mesh )
	{
		gdp->addTextureAttribute( GA_ATTRIB_VERTEX );
		GEO_AttributeHandle auv = gdp->getAttribute( GA_ATTRIB_VERTEX, "uv" );
		GEO_AttributeHandle as = gdp->getAttribute( GA_ATTRIB_VERTEX, "s" );
		GEO_AttributeHandle at = gdp->getAttribute( GA_ATTRIB_VERTEX, "t" );
		if ( auv.isAttributeValid() && as.isAttributeValid() && at.isAttributeValid() )
		{
			GA_GBPrimitiveIterator it( *gdp );
			GA_Primitive *p = it.getPrimitive();
			while ( p )
			{
				for (int i = 0; i < p->getVertexCount(); ++i)
				{
					GA_Offset v = p->getVertexOffset(i);
					as.setVertex(v);
					at.setVertex(v);
					auv.setVertex(v);
					auv.setF( as.getF(0), 0 );
					auv.setF( ((at.getF(0) * -1.0f) + 1.0f), 1 ); // wat, t arrives upside down for some reason.
					auv.setF( 0.0f, 2 );
				}
				++it;
				p = it.getPrimitive();
			}
		}
	}

	if ( m_renderer->m_motionType == RendererImplementation::Geometry )
	{
		msg(Msg::Debug, "IECoreMantra::ProceduralPrimitive::addVisibleRenderable", "MotionBlur:Geometry" );
		if ( !m_renderer->m_motionTimes.empty() )
		{
			if ( (size_t)m_renderer->m_motionSize == m_renderer->m_motionTimes.size() )
			{
				openGeometryObject();
			}
			addGeometry(gdp, m_renderer->m_motionTimes.front());
			m_renderer->m_motionTimes.pop_front();
			if ( m_renderer->m_motionTimes.empty() )
			{
				applySettings();
				closeObject();
			}
		}
	}
	else if ( m_renderer->m_motionType == RendererImplementation::ConcatTransform ||
			  m_renderer->m_motionType == RendererImplementation::SetTransform )
	{
		// It isn't clear that this will give correct results. 
		// ConcatTransform may need to interpolate transform snapshots.
		msg(Msg::Debug, "IECoreMantra::ProceduralPrimitive::addVisibleRenderable", "MotionBlur:Transform" );
		openGeometryObject();
			addGeometry(gdp, 0.0f);
			while ( !m_renderer->m_motionTimes.empty() )
			{
				setPreTransform( convert< UT_Matrix4T<float> >(m_renderer->m_motionTransforms.front()),
								 m_renderer->m_motionTimes.front() );
				m_renderer->m_motionTimes.pop_front();
				m_renderer->m_motionTransforms.pop_front();
			}
			applySettings();
		closeObject();
		m_renderer->m_motionType = RendererImplementation::Unknown;
	}
	else if ( m_renderer->m_motionType == RendererImplementation::Velocity )
	{
		msg(Msg::Debug, "IECoreMantra::ProceduralPrimitive::addVisibleRenderable", "MotionBlur:Velocity" );
		openGeometryObject();
			addGeometry(gdp, 0.0f);
			addVelocityBlurGeometry(gdp, m_preBlur, m_postBlur);
			applySettings();
		closeObject();
		m_renderer->m_motionType = RendererImplementation::Unknown;
	}
	else
	{
		msg(Msg::Debug, "IECoreMantra::ProceduralPrimitive::addVisibleRenderable", "MotionBlur:None" );
		openGeometryObject();
			addGeometry( gdp, 0.0f );
			setPreTransform( convert< UT_Matrix4T<float> >(m_renderer->m_transformStack.top()), 0.0f);
			applySettings();
		closeObject();
	}
}
bool ToHoudiniGroupConverter::doConversion( const VisibleRenderable *renderable, GU_Detail *geo ) const
{
    const Group *group = IECore::runTimeCast<const Group>( renderable );
    if ( !group )
    {
        return false;
    }

    Imath::M44f transform = ( runTimeCast<const M44fData>( m_transformParameter->getValue() ) )->readable();
    const Transform *groupTransform = group->getTransform();
    if ( groupTransform )
    {
        transform = transform * groupTransform->transform();
    }

    TransformOpPtr transformOp = new TransformOp();
    M44fDataPtr transformData = new M44fData( transform );
    transformOp->matrixParameter()->setValue( transformData );

    std::string groupName = nameParameter()->getTypedValue();
    if ( groupName == "" )
    {
        // backwards compatibility with older data
        if ( const StringData *groupNameData = group->blindData()->member<StringData>( "name" ) )
        {
            groupName = groupNameData->readable();
        }
    }

    const std::string &attribFilter = attributeFilterParameter()->getTypedValue();
    bool convertStandardAttributes = convertStandardAttributesParameter()->getTypedValue();

    size_t i = 0;
    const Group::ChildContainer &children = group->children();
    for ( Group::ChildContainer::const_iterator it=children.begin(); it != children.end(); ++it, ++i )
    {
        ConstVisibleRenderablePtr child = *it;

        ConstPrimitivePtr primitive = runTimeCast<const Primitive>( child );
        if ( primitive )
        {
            transformOp->inputParameter()->setValue( constPointerCast<Primitive>( primitive ) );
            child = staticPointerCast<VisibleRenderable>( transformOp->operate() );
        }

        ToHoudiniGeometryConverterPtr converter = ToHoudiniGeometryConverter::create( child );
        if ( !converter )
        {
            continue;
        }

        std::string name = groupName;
        if ( const StringData *childNameData = child->blindData()->member<StringData>( "name" ) )
        {
            const std::string &childName = childNameData->readable();
            if ( childName != "" )
            {
                if ( groupName != "" )
                {
                    name += "/";
                }

                name += childName;
            }
        }

        converter->nameParameter()->setTypedValue( name );
        converter->attributeFilterParameter()->setTypedValue( attribFilter );
        converter->convertStandardAttributesParameter()->setTypedValue( convertStandardAttributes );

        ToHoudiniGroupConverter *groupConverter = runTimeCast<ToHoudiniGroupConverter>( converter );
        if ( groupConverter )
        {
            groupConverter->transformParameter()->setValue( transformData );
        }

        GU_DetailHandle handle;
        handle.allocateAndSet( geo, false );

        if ( !converter->convert( handle ) )
        {
            continue;
        }
    }

    return true;
}
bool SOP_SceneCacheSource::convertObject( const IECore::Object *object, const std::string &name, const SceneInterface *scene, Parameters &params )
{
	ToHoudiniGeometryConverterPtr converter = 0;
	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 );
		
		if ( params.fullPathName != "" )
		{
			// 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 ( 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();
			}
		}
		
		return status;
	}
	catch ( std::exception &e )
	{
		addWarning( SOP_MESSAGE, e.what() );
		return false;
	}
	catch ( ... )
	{
		addWarning( SOP_MESSAGE, "Conversion failed for unknown reasons" );
		return false;
	}
}