void wendy_GIO_ROP::save_mata_ass(string arDsoPath,string dpCachePath,GU_Detail *gdp )
{
    //get arnold dso path


    g_particle_mataAss assROP;
    ofstream fout;
    string getCacheDP=dpCachePath;

    stringstream ss(getCacheDP);
    string sub_str;
    vector <string> sp_strPath;
    sp_strPath.clear();
    while(getline(ss,sub_str,'.'))
    {
        sp_strPath.push_back(sub_str);
    }

    string newAssPathName = sp_strPath[0]+"."+sp_strPath[1]+string(".ass");
    fout.open(newAssPathName);
    assROP.setGioCachePath(getCacheDP);  // set Link GIO CACHE
    for (GA_AttributeDict::iterator it = gdp->getAttributeDict(GA_ATTRIB_POINT).begin(GA_SCOPE_PUBLIC); !it.atEnd(); ++it)
    {
        GA_Attribute *attrib = it.attrib();
        string attName( attrib->getName() );
        assROP.inserExtraMataInfo(attName);
    }
    //set ass bbox
    UT_BoundingBox BBOX;
    gdp->getBBox(&BBOX);
    BBOX.expandBounds(2,2,2);
    float min_x=BBOX.xmin();
    float min_y=BBOX.xmin();
    float min_z=BBOX.xmin();
    float max_x=BBOX.xmax();
    float max_y=BBOX.ymax();
    float max_z=BBOX.zmax();
    assROP.setBBOX(min_x,min_y,min_z,max_x,max_y,max_z);
    assROP.setDsoPath(arDsoPath);
    assROP.save(fout);
    fout.close();
}
static void updateAttributeList(g_global_io &io,GU_Detail *cur_gdp)
{
    fpreal numPt= cur_gdp->getNumPoints();
    GA_Iterator iter(cur_gdp->getPointRange());
    fpreal start_point_num = *iter;

    if ( cur_gdp )
    {
        for (GA_AttributeDict::iterator it = cur_gdp->getAttributeDict(GA_ATTRIB_POINT).begin(GA_SCOPE_PUBLIC); !it.atEnd(); ++it)
        {
            GA_Attribute *attrib = it.attrib();
            UT_String attName( attrib->getName() );
            // std::cout<<"the att name is"<<attName<<std::endl;
            int attSize = attrib->getTupleSize();
            GA_StorageClass     storage= attrib->getStorageClass();
            GA_TypeInfo typeInfo = attrib->getTypeInfo();

            //save mata info



            switch(attSize)
            {
            case 1:                    // per  int or float
                if(storage==GA_STORECLASS_FLOAT)
                {
                    std::cout<<"the att name is "<<attName<< " and the type is float\n";
                    g_particles_io t_io;
                    t_io.GIO_SetCurrentAttributeName(attName);
                    t_io.GIO_SetCurrentAttributeType(g_particles_io::FLT_TYPE);
                    std::vector<float> t_value;  // create empty stack
                    forkTheFLTData(t_value,attrib,numPt,start_point_num);
                    t_io.GIO_setFLTAttributeList(t_value);
                    io.GIO_installParticleHandle(t_io);  // install this attribute into our global IO

                }
                if(storage==GA_STORECLASS_INT)
                {
                    std::cout<<"the att name is "<<attName<< " and the type is int\n";
                    g_particles_io t_io;
                    t_io.GIO_SetCurrentAttributeName(attName);
                    t_io.GIO_SetCurrentAttributeType(g_particles_io::INT_TYPE);
                    std::vector<int> t_value;  // create empty  int stack
                    forkTheINTData(t_value,attrib,numPt,start_point_num);
                    t_io.GIO_setINTAttributeList(t_value);
                    io.GIO_installParticleHandle(t_io);
                }
                break;
            case 3:                   // int vector or float vector
                if(storage==GA_STORECLASS_FLOAT)
                {
                    std::cout<<"the att name is "<<attName<< " and the type is float vector\n";
                    g_particles_io t_io;
                    t_io.GIO_SetCurrentAttributeName(attName);
                    t_io.GIO_SetCurrentAttributeType(g_particles_io::FLT_VEC_TYPE);
                    std::vector<per_float_vector> t_value;  // create empty  int stack
                    forkTheFLTVectorData(t_value,attrib,numPt,start_point_num);
                    t_io.GIO_setFLTVecAttributeList(t_value);
                    io.GIO_installParticleHandle(t_io);

                }
                if(storage==GA_STORECLASS_INT)
                {
                    std::cout<<"the att name is "<<attName<< " and the type is int vector\n";
                    g_particles_io t_io;
                    t_io.GIO_SetCurrentAttributeName(attName);
                    t_io.GIO_SetCurrentAttributeType(g_particles_io::INT_VEC_TYPE);
                    std::vector<per_int_vector> t_value;  // create empty  int stack
                    forkTheINTVectorData(t_value,attrib,numPt,start_point_num);
                    t_io.GIO_setINTVecAttributeList(t_value);
                    io.GIO_installParticleHandle(t_io);
                }
                break;
            case 4:                   // P attrib
            {
                std::cout<<"the att name is "<<attName<< " and the type is float 4 \n";
                g_particles_io t_io;
                t_io.GIO_SetCurrentAttributeName(attName);
                t_io.GIO_SetCurrentAttributeType(g_particles_io::FLT_VEC_TYPE);
                std::vector<per_float_vector> t_value;  // create empty  int stack
                forkTheFLTVectorData(t_value,it.attrib(),numPt,start_point_num);

                t_io.GIO_setFLTVecAttributeList(t_value);
                io.GIO_installParticleHandle(t_io);
            }

            break;
            }

        }
    }
}
OP_ERROR
SOP_Smoke_Source::cookMySop(OP_Context &context)
{
    // We must lock our inputs before we try to access their geometry.
    // OP_AutoLockInputs will automatically unlock our inputs when we return.
    // NOTE: Don't call unlockInputs yourself when using this!
    OP_AutoLockInputs inputs(this);
    if (inputs.lock(context) >= UT_ERROR_ABORT)
        return error();

    fpreal now = context.getTime();

    duplicateSource(0, context);

    // These three lines enable the local variable support.  This allows
    // $CR to get the red colour, for example, as well as supporting
    // any varmap created by the Attribute Create SOP.
    // Note that if you override evalVariableValue for your own
    // local variables (like SOP_Star does) it is essential you
    // still call the SOP_Node::evalVariableValue or you'll not
    // get any of the benefit of the built in local variables.

    // The variable order controls precedence for which attribute will be
    // be bound first if the same named variable shows up in multiple
    // places.  This ordering ensures point attributes get precedence.
    setVariableOrder(3, 2, 0, 1);

    // The setCur* functions track which part of the gdp is currently
    // being processed - it is what is used in the evalVariableValue
    // callback as the current point.  The 0 is for the first input,
    // you can have two inputs so $CR2 would get the second input's
    // value.
    setCurGdh(0, myGdpHandle);

    // Builds the lookup table matching attributes to the local variables.
    setupLocalVars();

    // Here we determine which groups we have to work on.  We only
    //	handle point groups.
    if (error() < UT_ERROR_ABORT && cookInputGroups(context) < UT_ERROR_ABORT &&
        (!myGroup || !myGroup->isEmpty()))
    {
        UT_AutoInterrupt progress("Flattening Points");

        // Handle all position, normal, and vector attributes.
        // It's not entirely clear what to do for quaternion or transform attributes.
        // We bump the data IDs of the attributes to modify in advance,
        // since we're already looping over them, and we want to avoid
        // bumping them all for each point, in case that's slow.
        UT_Array<GA_RWHandleV3> positionattribs(1);
        UT_Array<GA_RWHandleV3> normalattribs;
        UT_Array<GA_RWHandleV3> vectorattribs;
        GA_Attribute *attrib;
        GA_FOR_ALL_POINT_ATTRIBUTES(gdp, attrib)
        {
            // Skip non-transforming attributes
            if (!attrib->needsTransform())
                continue;

            GA_TypeInfo typeinfo = attrib->getTypeInfo();
            if (typeinfo == GA_TYPE_POINT || typeinfo == GA_TYPE_HPOINT)
            {
                GA_RWHandleV3 handle(attrib);
                if (handle.isValid())
                {
                    positionattribs.append(handle);
                    attrib->bumpDataId();
                }
            }
            else if (typeinfo == GA_TYPE_NORMAL)
            {
                GA_RWHandleV3 handle(attrib);
                if (handle.isValid())
                {
                    normalattribs.append(handle);
                    attrib->bumpDataId();
                }
            }
            else if (typeinfo == GA_TYPE_VECTOR)
            {
                GA_RWHandleV3 handle(attrib);
                if (handle.isValid())
                {
                    vectorattribs.append(handle);
                    attrib->bumpDataId();
                }
            }
        }

        // Iterate over points up to GA_PAGE_SIZE at a time using blockAdvance.
        GA_Offset start;
        GA_Offset end;
        for (GA_Iterator it(gdp->getPointRange(myGroup)); it.blockAdvance(start, end);)
        {
            // Check if user requested abort
            if (progress.wasInterrupted())
                break;

            for (GA_Offset ptoff = start; ptoff < end; ++ptoff)
            {
                // This sets the current point that is beint processed to
                // ptoff.  This means that ptoff will be used for any
                // local variable for any parameter evaluation that occurs
                // after this point.
                // NOTE: Local variables and repeated parameter evaluation
                //       is significantly slower and sometimes more complicated
                //       than having a string parameter that specifies the name
                //       of an attribute whose values should be used instead.
                //       That parameter would only need to be evaluated once,
                //       the attribute could be looked up once, and quickly
                //       accessed; however, a separate point attribute would
                //       be needed for each property that varies per point.
                //       Local variable evaluation isn't threadsafe either,
                //       whereas attributes can be read safely from multiple
                //       threads.
                //
                //       Long story short: *Local variables are terrible.*
                myCurPtOff[0] = ptoff;
                float dist = DIST(now);
                UT_Vector3 normal;
                if (!DIRPOP())
                {
                    switch (ORIENT())
                    {
                        case 0 : // XY Plane
                            normal.assign(0, 0, 1);
                            break;
                        case 1 : // YZ Plane
                            normal.assign(1, 0, 0);
                            break;
                        case 2 : // XZ Plane
                            normal.assign(0, 1, 0);
                            break;
                    }
                }
                else
                {
                    normal.assign(NX(now), NY(now), NZ(now));
                    normal.normalize();
                }

                // Project positions onto the plane by subtracting
                // off the normal component.
                for (exint i = 0; i < positionattribs.size(); ++i)
                {
                    UT_Vector3 p = positionattribs(i).get(ptoff);
                    p -= normal * (dot(normal, p) - dist);
                    positionattribs(i).set(ptoff, p);
                }

                // Normals will now all either be normal or -normal.
                for (exint i = 0; i < normalattribs.size(); ++i)
                {
                    UT_Vector3 n = normalattribs(i).get(ptoff);
                    if (dot(normal, n) < 0)
                        n = -normal;
                    else
                        n = normal;
                    normalattribs(i).set(ptoff, n);
                }

                // Project vectors onto the plane through the origin by
                // subtracting off the normal component.
                for (exint i = 0; i < vectorattribs.size(); ++i)
                {
                    UT_Vector3 v = vectorattribs(i).get(ptoff);
                    v -= normal * dot(normal, v);
                    vectorattribs(i).set(ptoff, v);
                }
            }
        }
    }
static void exportParticlesDetail( const GU_Detail* gdp,
                                  const std::string& filePath,
                                  const std::map<std::string,
                                  channel_type>& desiredChannels )
{
	prt_ofstream ostream;

	static std::map<std::string, std::string> s_reservedChannels;
	if( s_reservedChannels.empty() ) {
		s_reservedChannels[ gdp->getStdAttributeName( GEO_ATTRIBUTE_NORMAL ) ] = "Normal";
		s_reservedChannels[ gdp->getStdAttributeName( GEO_ATTRIBUTE_TEXTURE ) ] = "TextureCoord";
		s_reservedChannels[ gdp->getStdAttributeName( GEO_ATTRIBUTE_VELOCITY ) ] = "Velocity";
		s_reservedChannels[ gdp->getStdAttributeName( GEO_ATTRIBUTE_DIFFUSE ) ] = "Color";
		//s_reservedChannels[ gdp->getStdAttributeName( GEO_ATTRIBUTE_ALPHA ) ] = "Density";
		//s_reservedChannels[ gdp->getStdAttributeName( GEO_ATTRIBUTE_MASS ) ] = "Density";
		s_reservedChannels[ gdp->getStdAttributeName( GEO_ATTRIBUTE_LIFE ) ] = "";
		s_reservedChannels[ gdp->getStdAttributeName( GEO_ATTRIBUTE_ID ) ] = "ID";
		s_reservedChannels[ gdp->getStdAttributeName( GEO_ATTRIBUTE_PSCALE ) ] = "Scale";
		s_reservedChannels[ "accel" ] = "Acceleration";
	}

	float posVal[3];
	float lifeVal[2];
	
	ostream.bind( "Position", posVal, 3 );

	//We handle the life channel in a special manner
	GA_ROAttributeRef lifeAttrib = gdp->findPointAttribute( gdp->getStdAttributeName( GEO_ATTRIBUTE_LIFE ) );
	if( lifeAttrib.isValid() ){
		std::map<std::string,channel_type>::const_iterator it;
		
		it = desiredChannels.find( "Age" );
		if( it != desiredChannels.end() && it->second.second == 1 )
			ostream.bind( "Age", &lifeVal[0], 1, it->second.first );
		else if( desiredChannels.empty() )
			ostream.bind( "Age", &lifeVal[0], 1, prtio::data_types::type_float16 );

		it = desiredChannels.find( "LifeSpan" );
		if( it != desiredChannels.end() && it->second.second == 1 )
			ostream.bind( "LifeSpan", &lifeVal[1], 1, it->second.first );
		else if( desiredChannels.empty() )
			ostream.bind( "LifeSpan", &lifeVal[1], 1, prtio::data_types::type_float16 );
	}
	
	//Using a deque to prevent the memory from moving around after adding the bound_attribute to the container.
	std::deque< bound_attribute<int> > m_intAttrs;
	std::deque< bound_attribute<float> > m_floatAttrs;
	std::deque< bound_attribute<float> > m_vectorAttrs;
	
	for ( GA_AttributeDict::iterator it = gdp->getAttributes().getDict(GA_ATTRIB_POINT).begin(GA_SCOPE_PUBLIC); !it.atEnd(); ++it) {
		
		GA_Attribute *node = it.attrib();

		std::string channelName = node->getName();

		//Translate special names
		std::map<std::string,std::string>::const_iterator itResChannel = s_reservedChannels.find( channelName );
		if( itResChannel != s_reservedChannels.end() ){
			//If its empty, that means we reserve some sort of special handling.
			if( itResChannel->second.empty() )
				continue;
			channelName = itResChannel->second;
		}
		
		//Skip channels that aren't on the list.
		std::map<std::string,channel_type>::const_iterator itChannel = desiredChannels.find( channelName );
		bool channelIsDesired = ( itChannel != desiredChannels.end() );
		
		if( !desiredChannels.empty() && !channelIsDesired )
			continue;
			
		prtio::data_types::enum_t type;
		
		//Only add valid channel names
		if( detail::is_valid_channel_name( channelName.c_str() ) ) {
			//I add the new item to the deque, THEN initialize it since a deque will not move the object around and this allows
			//me to allocate the float array and not have to worry about the object getting deleted too early.
			switch( node->getStorageClass() ){
			case GA_STORECLASS_FLOAT:
				if( node->getTupleSize()==3 ){
					m_vectorAttrs.push_back( bound_attribute<float>() );
					m_vectorAttrs.back().attr =	gdp->findPointAttribute(node->getName());
					m_vectorAttrs.back().count = node->getTupleSize();
					m_vectorAttrs.back().data = new float[m_vectorAttrs.back().count];

					type = prtio::data_types::type_float16;
					if( channelIsDesired ){
						type = itChannel->second.first;
						if( itChannel->second.second != m_vectorAttrs.back().count )
							continue;
					}

					ostream.bind( channelName, m_vectorAttrs.back().data, m_vectorAttrs.back().count, type );

				} else {
					m_floatAttrs.push_back( bound_attribute<float>() );
					m_floatAttrs.back().attr =	gdp->findPointAttribute( node->getName() );
					m_floatAttrs.back().count = node->getTupleSize();
					m_floatAttrs.back().data = new float[m_floatAttrs.back().count];

					type = prtio::data_types::type_float16;
					if( channelIsDesired ){
						type = itChannel->second.first;
						if( itChannel->second.second != m_floatAttrs.back().count )
							continue;
					}

					ostream.bind( channelName, m_floatAttrs.back().data, m_floatAttrs.back().count, type );
				}
				break;
			case GA_STORECLASS_INT:
				m_intAttrs.push_back( bound_attribute<int>() );
				m_intAttrs.back().attr = gdp->findPointAttribute( node->getName() );
				m_intAttrs.back().count = node->getTupleSize();
				m_intAttrs.back().data = new int[m_intAttrs.back().count];

				type = prtio::data_types::type_int32;
				if( channelIsDesired ){
					type = itChannel->second.first;
					if( itChannel->second.second != m_intAttrs.back().count )
						continue;
				}
			
				ostream.bind( channelName, m_intAttrs.back().data, m_intAttrs.back().count, type );
				break;
			default:
				break;
			}
		}
	}

	try{
		ostream.open( filePath );
	} catch( const std::ios::failure& e ) {
		std::cerr << e.what() << std::endl;
		throw HOM_OperationFailed( "Failed to open the file" );
	}
		
	GA_IndexMap map = gdp->getPointMap();
	UT_Vector3 p;
	GEO_Point* pt;
	GA_Index indexSize = map.indexSize();
	GA_Offset offset;

	for( int i = 0 ; i < indexSize; i++ ) {
		offset = map.offsetFromIndex( i );
		p = gdp->getPos3( offset );
		posVal[0] = p.x();
		posVal[1] = p.y();
		posVal[2] = -1 * p.z();
		
		//TODO: Remove the GEO_Point object that is now deprecated. 
		pt = ( GEO_Point* )gdp->getGBPoint( offset );
		
		//TODO: Convert this into appropriate time values. Is it seconds or frames or what?!
		if( lifeAttrib.isValid() ) 
			pt->get( lifeAttrib, lifeVal, 2 );

		for( std::deque< bound_attribute<float> >::iterator it = m_floatAttrs.begin(), itEnd = m_floatAttrs.end(); it != itEnd; ++it )
			pt->get( it->attr, it->data, it->count );

		for( std::deque< bound_attribute<float> >::iterator it = m_vectorAttrs.begin(), itEnd = m_vectorAttrs.end(); it != itEnd; ++it ) {
			pt->get( it->attr, it->data, it->count );
				
			//TODO: Optionally transform into some consistent world space for PRT files.
		}

		for( std::deque< bound_attribute<int> >::iterator it = m_intAttrs.begin(), itEnd = m_intAttrs.end(); it != itEnd; ++it )
			pt->get( it->attr, it->data, it->count );

		ostream.write_next_particle();
	}
	
	ostream.close();
}
/* static */
GU_PrimPacked* 
GusdGU_PackedUSD::Build( 
    GU_Detail&              detail, 
    const UT_StringHolder&  fileName, 
    const SdfPath&          primPath, 
    UsdTimeCode             frame, 
    const char*             lod,
    GusdPurposeSet          purposes,
    const UsdPrim&          prim,
    const UT_Matrix4D*      xform )
{   
    auto packedPrim = GU_PrimPacked::build( detail, k_typeName );
    auto impl = UTverify_cast<GusdGU_PackedUSD *>(packedPrim->implementation());
    impl->m_fileName = fileName;
    impl->m_primPath = primPath;
    impl->m_frame = frame;

    if( prim && !prim.IsA<UsdGeomBoundable>() )
    {
        UsdGeomImageable geom = UsdGeomImageable(prim);
        std::vector<UsdGeomPrimvar> authoredPrimvars = geom.GetAuthoredPrimvars();
        GT_DataArrayHandle buffer;

        for( const UsdGeomPrimvar &primvar : authoredPrimvars ) {
            // XXX This is temporary code, we need to factor the usd read code into GT_Utils.cpp
            // to avoid duplicates and read for types GfHalf,double,int,string ...
            GT_DataArrayHandle gtData = GusdPrimWrapper::convertPrimvarData( primvar, frame );
	    if (!gtData)
		continue;

            const UT_String  name(primvar.GetPrimvarName());
            const GT_Storage gtStorage = gtData->getStorage();
            const GT_Size    gtTupleSize = gtData->getTupleSize();

            GA_Attribute *anAttr = detail.addTuple(GT_Util::getGAStorage(gtStorage), GA_ATTRIB_PRIMITIVE, name,
                                                   gtTupleSize);

            if( !anAttr ) {
                // addTuple could fail for various reasons, like if there's a
                // non-alphanumeric character in the primvar name.
                continue;
            }

            if( const GA_AIFTuple *aIFTuple = anAttr->getAIFTuple()) {

                const float* flatArray = gtData->getF32Array( buffer );
                aIFTuple->set( anAttr, packedPrim->getMapOffset(), flatArray, gtTupleSize );

            }  else {

                //TF_WARN( "Unsupported primvar type: %s, %s, tupleSize = %zd", 
                //         GT_String( name ), GTstorage( gtStorage ), gtTupleSize );
            }
        }
    }

    if( lod )
    {
#if SYS_VERSION_FULL_INT < 0x10050000
        impl->intrinsicSetViewportLOD( lod );
#else
        impl->intrinsicSetViewportLOD( packedPrim, lod );
#endif
    }
    impl->setPurposes( purposes );

    // It seems that Houdini may reuse memory for packed implementations with
    // out calling the constructor to initialize data. 
    impl->resetCaches();

    // If a UsdPrim was passed in, make sure it is used.
    impl->m_usdPrim = prim;

    if (xform) {
        impl->setTransform(*xform);
    } else {
        impl->updateTransform();    
    }
    return packedPrim;
}
/// Cook the SOP! This method does all the work
OP_ERROR SOP_OpHolder::cookMySop( OP_Context &context )
{
	IECore::MessageHandler::Scope handlerScope( getMessageHandler() );
	
	// some defaults and useful variables
	Imath::Box3f bbox( Imath::V3f(-1,-1,-1), Imath::V3f(1,1,1) );
	float now = context.getTime();

	// force eval of our nodes parameters with our hidden parameter expression
	evalInt( "__evaluateParameters", 0, now );

	// get our op
	IECore::OpPtr op = IECore::runTimeCast<IECore::Op>( getParameterised() );
	
	// check for a valid parameterised on this SOP
	if ( !op )
	{
		UT_String msg( "Op Holder has no parameterised class to operate on!" );
		addError( SOP_MESSAGE, msg );
		return error();
	}

	if( lockInputs(context)>=UT_ERROR_ABORT )
	{
		return error();
	}

	// start our work
	UT_Interrupt *boss = UTgetInterrupt();
	boss->opStart("Building OpHolder Geometry...");
	gdp->clearAndDestroy();
	
	setParameterisedValues( now );
	
	try
	{
		// make our Cortex op do it's thing...
		op->operate();

		// pass ourselves onto the GR_Cortex render hook
		IECoreHoudini::NodePassData data( this, IECoreHoudini::NodePassData::CORTEX_OPHOLDER );
		GA_RWAttributeRef attrRef = gdp->createAttribute( GA_ATTRIB_DETAIL, GA_SCOPE_PRIVATE, "IECoreHoudiniNodePassData", NULL, NULL, "blinddata" );
		GA_Attribute *attr = attrRef.getAttribute();
		const GA_AIFBlindData *blindData = attr->getAIFBlindData();
		blindData->setDataSize( attr, sizeof(IECoreHoudini::NodePassData), &data );

		// if our result is a visible renderable then set our bounds on our output gdp
		const IECore::Object *result = op->resultParameter()->getValue();
		IECore::ConstVisibleRenderablePtr renderable = IECore::runTimeCast<const IECore::VisibleRenderable>( result );
		if ( renderable )
		{
			Imath::Box3f bbox = renderable->bound();
			gdp->cube( bbox.min.x, bbox.max.x, bbox.min.y, bbox.max.y, bbox.min.z, bbox.max.z, 0, 0, 0, 1, 1 );
		}
	}
	catch( boost::python::error_already_set )
	{
		addError( SOP_MESSAGE, "Error raised during Python evaluation!" );
		IECorePython::ScopedGILLock lock;
		PyErr_Print();
	}
	catch( const IECore::Exception &e )
	{
		addError( SOP_MESSAGE, e.what() );
	}
	catch( const std::exception &e )
	{
		addError( SOP_MESSAGE, e.what() );
	}
	catch( ... )
	{
		addError( SOP_MESSAGE, "Caught unknown exception!" );
	}

	// tidy up & go home!
	boss->opEnd();
	unlockInputs();
	return error();
}