/* static */
bool 
GusdWriteCtrlFlags::getBoolAttr( 
    const GT_PrimitiveHandle& prim,
    const char *attrName,
    bool defaultValue ) 
{   
    if( prim ) {
        GT_DataArrayHandle data;
        if( prim->getPrimitiveType() == GT_GEO_PACKED ) {
            GT_AttributeListHandle instAttrs = 
                UTverify_cast<const GT_GEOPrimPacked*>(prim.get())->getInstanceAttributes();
            if( instAttrs ) {
                data = instAttrs->get( attrName );
            }    
        }
        if( !data ) {
            GT_Owner own;
            data = prim->findAttribute( attrName, own, 0 );
        }
        if( data ) {
            return bool( data->getI32(0) );
        }

    }
    return defaultValue;
}
示例#2
0
bool
GusdPrimWrapper::updatePrimvarFromGTPrim( 
    const TfToken&            name,
    const GT_Owner&           owner,
    const TfToken&            interpolation,
    UsdTimeCode               time,
    const GT_DataArrayHandle& dataIn )
{
    GT_DataArrayHandle data = dataIn;
    UsdGeomImageable prim( getUsdPrimForWrite() );

    // cerr << "updatePrimvarFromGTPrim: " 
    //         << prim.GetPrim().GetPath() << ":" << name << ", " << interpolation 
    //         << ", entries = " << dataIn->entries() << endl;

    AttrLastValueKeyType key(owner, name);
    auto it = m_lastAttrValueDict.find( key );
    if( it == m_lastAttrValueDict.end() ) {

        // If we're creating an overlay this primvar might already be
        // authored on the prim. If the primvar is indexed we need to 
        // block the indices attribute, because we flatten indexed
        // primvars.
        if( UsdGeomPrimvar primvar = prim.GetPrimvar(name) ) {
            if( primvar.IsIndexed() ) {
                primvar.BlockIndices();
            }
        }

        m_lastAttrValueDict.insert(
            std::make_pair(key, 
                       AttrLastValueEntry( time, data->harden())));

        GusdGT_Utils::setPrimvarSample( prim, name, data, interpolation, time );
    }
    else {
        AttrLastValueEntry& entry = it->second;
        if( data->isEqual( *entry.data )) {
            entry.lastCompared = time;
            return false;
        }
        else {
            if( entry.lastCompared != entry.lastSet ) {
                GusdGT_Utils::setPrimvarSample( prim, name, entry.data, interpolation, entry.lastCompared );
            }
            
             if( UsdGeomPrimvar primvar = prim.GetPrimvar(name) ) {
                if( primvar.IsIndexed() ) {
                    primvar.BlockIndices();
                }
            }

            GusdGT_Utils::setPrimvarSample( prim, name, data, interpolation, time );
            entry.data = data->harden();
            entry.lastSet = entry.lastCompared = time;
            return true;
        }
    }
    return true;
}
示例#3
0
void
GusdPrimWrapper::updateVisibilityFromGTPrim(
        const GT_PrimitiveHandle& sourcePrim,
        UsdTimeCode time,
        bool forceWrite )
{
    // If we're tracking visibility, set this prim's default state to
    // invisible. File-per-frame exports rely on this if the prim isn't
    // persistent throughout the frame range.
    GT_Owner attrOwner;
    GT_DataArrayHandle houAttr
        = sourcePrim->findAttribute(GUSD_VISIBLE_ATTR, attrOwner, 0);
    if(houAttr) {
        int visible = houAttr->getI32(0);
        if(visible) {
            setVisibility(UsdGeomTokens->inherited, time);
        } else {
            setVisibility(UsdGeomTokens->invisible, time);
        }
    }
    else if ( forceWrite ) {
        if(isVisible()) {
            setVisibility(UsdGeomTokens->inherited, time);
        } else {
            setVisibility(UsdGeomTokens->invisible, time);
        }
    }
}
示例#4
0
void
GusdPrimWrapper::updateVisibilityFromGTPrim(
        const GT_PrimitiveHandle& sourcePrim,
        UsdTimeCode time)
{
    // If we're tracking visibility, set this prim's default state to
    // invisible. File-per-frame exports rely on this if the prim isn't
    // persistent throughout the frame range.
    UsdAttribute visAttr = getUsdPrimForWrite().GetVisibilityAttr();
    if( visAttr.IsValid() )
        visAttr.Set(UsdGeomTokens->invisible,
                    UsdTimeCode::Default()); 
    GT_Owner attrOwner;
    GT_DataArrayHandle houAttr
        = sourcePrim->findAttribute("visible", attrOwner, 0);
    if(houAttr) {
        int visible = houAttr->getI32(0);
        if(visible) {
            setVisibility(UsdGeomTokens->inherited, time);
        } else {
            setVisibility(UsdGeomTokens->invisible, time);
        }
    }
    else {
        if(isVisible()) {
            setVisibility(UsdGeomTokens->inherited, time);
        } else {
            setVisibility(UsdGeomTokens->invisible, time);
        }
    }
}
示例#5
0
bool
GusdPrimWrapper::updateAttributeFromGTPrim( 
    GT_Owner owner, 
    const std::string& name,
    const GT_DataArrayHandle& houAttr, 
    UsdAttribute& usdAttr, 
    UsdTimeCode time )
{
    // return true if we need to set the value
    if( !houAttr || !usdAttr )
       return false;

    // Check to see if the current value of this attribute has changed 
    // from the last time we set the value.

    AttrLastValueKeyType key(owner, name);
    auto it = m_lastAttrValueDict.find( key );
    if( it == m_lastAttrValueDict.end()) { 

        // Set the value for the first time
        m_lastAttrValueDict.insert(
                std::make_pair(key, 
                               AttrLastValueEntry( time, houAttr->harden())));

        GusdGT_Utils::setUsdAttribute(usdAttr, houAttr, time);
        return true;
    } 
    else {
        AttrLastValueEntry& entry = it->second;
        if( houAttr->isEqual( *entry.data )) {

            // The value are the as before. Don't set.
            entry.lastCompared = time;
            return false;
        }
        else {
            if( entry.lastCompared != entry.lastSet ) {
                // Set a value on the last frame the previous value was valid.
                GusdGT_Utils::setUsdAttribute(usdAttr, entry.data, entry.lastCompared);
            }
            
            // set the new value
            GusdGT_Utils::setUsdAttribute(usdAttr, houAttr, time);

            // save this value to compare on later frames
            entry.data = houAttr->harden();
            entry.lastSet = entry.lastCompared = time;
            return true;
        }
    }
    return false;
}
示例#6
0
void
GusdPrimWrapper::updateActiveFromGTPrim(
        const GT_PrimitiveHandle& sourcePrim,
        UsdTimeCode time)
{
    UsdPrim prim = getUsdPrimForWrite().GetPrim();

    GT_Owner attrOwner;
    GT_DataArrayHandle houAttr
        = sourcePrim->findAttribute(GUSD_ACTIVE_ATTR, attrOwner, 0);
    if (houAttr) {
        int active = houAttr->getI32(0);
        prim.SetActive((bool)active);
    }
}
示例#7
0
void
GusdPrimWrapper::updateActiveFromGTPrim(
        const GT_PrimitiveHandle& sourcePrim,
        UsdTimeCode time)
{
    UsdPrim prim = getUsdPrim().GetPrim();

    GT_Owner attrOwner;
    GT_DataArrayHandle houAttr
        = sourcePrim->findAttribute(GUSD_ACTIVE_ATTR, attrOwner, 0);
    if (houAttr) {
        GT_String state = houAttr->getS(0);
        if (state) {
            if (strcmp(state, "active") == 0) {
                prim.SetActive(true);
            } else if (strcmp(state, "inactive") == 0) {
                prim.SetActive(false);
            }
        }
    }
}
示例#8
0
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 primvarPattern(Cd);

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

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

    {
        UsdGeomImageable prim = getUsdPrimForRead();

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

        // It's common for "Cd" to be the only primvar to load.
        // In this case, avoid getting all other authored primvars.
        if (primvarPattern == 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().HasAuthoredValueOpinion()) {
                    authoredPrimvars.push_back(colorPrimvar);
                }
            }
        } else if (primvarPattern != "") {
            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, 1, " ")) {
            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;
        }

        // 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( name.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( name.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( name.c_str(), gtData, true );
            }
        }
        else if( primvar.GetInterpolation() == UsdGeomTokens->constant )
        {
            if( constant ) {
                *constant = (*constant)->addAttribute( name.c_str(), gtData, true );
            }
        }
    }
}
/* 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;
}
示例#10
0
void 
GusdRefiner::addPrimitive( const GT_PrimitiveHandle& gtPrimIn )
{
    if(!gtPrimIn) {
        std::cout << "Attempting to add invalid prim" << std::endl;
        return;
    }
    GT_PrimitiveHandle gtPrim = gtPrimIn;     // copy to a non-const handle
    int primType = gtPrim->getPrimitiveType();
    DBG( cerr << "GusdRefiner::addPrimitive, " << gtPrim->className() << endl );
    
    string primName;
    // Types can register a function to provide a prim name. 
    // Volumes do this to return a name stored in the f3d file. This is 
    // important for consistant cluster naming.
    string n;
    if( GusdPrimWrapper::getPrimName( gtPrim, n )) {
        primName = n;
    }

    bool refinePackedPrims = m_refinePackedPrims;
    bool primHasNameAttr = false;
    if( primName.empty() ) {

        GT_AttributeListHandle primAttrs;
        if( primType == GT_GEO_PACKED ) {
            primAttrs = UTverify_cast<const GT_GEOPrimPacked*>(gtPrim.get())->getInstanceAttributes();

        } 
        if( !primAttrs ) {
            primAttrs = gtPrim->getUniformAttributes();
        }
        if( !primAttrs ) {
            primAttrs = gtPrim->getDetailAttributes();
        }

        GT_DataArrayHandle dah;
        if( primAttrs ) {
            dah = primAttrs->get( m_pathAttrName.c_str() );
        }

        if( dah && dah->isValid() ) {
            const char *s = dah->getS(0);
            if( s != NULL ) {
                primName = s;
                primHasNameAttr = true;
            }
        }
        if( primAttrs ) {
            GT_DataArrayHandle overXformsAttr = primAttrs->get( GUSD_OVERTRANSFORMS_ATTR );
            if( overXformsAttr ) {
                if( overXformsAttr->getI32(0) != 0 ) {
                    refinePackedPrims = false;
                }
            }
        }
    }

    
    // The following is only necessary for point instancers. Prototypes 
    // can't be point instancers.
    if (!m_buildPrototypes) {

        // Check per prim if we are building a point instancer. This may cause
        // problems for point instancers with discontiguous packed prims.
        bool localBuildPointInstancer = false;
        // If we have imported USD geometry get the type to see if it is a
        // point instancer we need to overlay.
        if(auto packedUSD = dynamic_cast<const GusdGT_PackedUSD*>( gtPrim.get() )) {
            if(packedUSD->getFileName()) {

                // Get the usd src prim path used for point instancers
                const SdfPath& instancerPrimPath =
                    packedUSD->getSrcPrimPath();

                GusdStageCacheReader cache;
                if(UsdPrim prim = cache.GetPrimWithVariants(
                    packedUSD->getFileName(), instancerPrimPath).first) {
                    // Get the type name of the usd file to overlay
                    m_pointInstancerType = prim.GetTypeName();
            
                    // Make sure to set buildPointInstancer to true if we are overlaying a
                    // point instancer
                    if (m_pointInstancerType == _tokens->PointInstancer ||
                        m_pointInstancerType == _tokens->PxPointInstancer) {
                        localBuildPointInstancer = true;
                    }
                }
            }
        }
        // If we find either an instancepath or usdinstancepath attribute, build a
        // point instancer.
        GT_Owner owner;
        if(gtPrim->findAttribute("instancepath", owner, 0) ||
            gtPrim->findAttribute("usdinstancepath", owner, 0) ) {
            localBuildPointInstancer = true;
        }

        if (m_buildPointInstancer || localBuildPointInstancer) {
            // If we are building point instancer, stash prims that can be 
            // point instanced. Build the point instancer in the finish method.
            
            // If given a prim path, pass it to the collector for a custom
            // usd scope. Otherwise pass an empty SdfPath.
            SdfPath instancerPrimPath;
            if( !primName.empty() ) {
                instancerPrimPath = SdfPath(createPrimPath(primName));
            }

            if( auto packedUSD = dynamic_cast<const GusdGT_PackedUSD*>( gtPrim.get() )) {
                // Point instancer from packed usd
                instancerPrimPath = instancerPrimPath.IsEmpty() ? packedUSD->getSrcPrimPath() : instancerPrimPath;
                m_collector.addInstPrim( instancerPrimPath, gtPrim );
                return;
            }
            else if( gtPrim->getPrimitiveType() == GT_PRIM_INSTANCE ) {
                // Point instancer from packed primitives

                // A GT_PrimInstance can container more than one instance. Create 
                // an entry for each.
                auto instPrim = UTverify_cast<const GT_PrimInstance*>( gtPrim.get() );

                // TODO: If we put all geometry packed prims here, then we break
                // grouping prims for purpose
                for( size_t i = 0; i < instPrim->entries(); ++i ) {
                    m_collector.addInstPrim( instancerPrimPath, gtPrim, i );
                }
                return;
            }

            if( primType == GT_PRIM_PARTICLE || primType == GT_PRIM_POINT_MESH ) {
                // Point instancer from points with instancepath attribute

                // Check for the usdprototypespath attribute in case it is not
                // a point or primitivie attribute.
                GT_AttributeListHandle uniformAttrs = gtPrim->getUniformAttributes();
                uniformAttrs = findAndAddStringAttribute(uniformAttrs, "usdprototypespath", gtPrim);

                // Find and add a custom prototype scope attribute.
                uniformAttrs = findAndAddStringAttribute(uniformAttrs, "usdprototypesscope", gtPrim);

                gtPrim = new GusdGT_PointInstancer( 
                                    gtPrim->getPointAttributes(), 
                                    uniformAttrs );
                primType = gtPrim->getPrimitiveType();
            }
        }
    }
    // We must refine packed prims that don't have a name
    if( !primHasNameAttr && !refinePackedPrims ) {
        refinePackedPrims = true;
    }

    if( primName.empty() && 
        gtPrim->getPrimitiveType() == GusdGT_PackedUSD::getStaticPrimitiveType() ) {

        auto packedUsdPrim = UTverify_cast<const GusdGT_PackedUSD *>(gtPrim.get());
        SdfPath path = packedUsdPrim->getPrimPath().StripAllVariantSelections();
        if( m_useUSDIntrinsicNames ) {
            primName = path.GetString();
        }
        else {
            primName = path.GetName();
        }

        // We want prototypes to be children of the point instancer, so we make 
        // the usd path a relative scope of just the usd prim name
        if ( m_buildPrototypes && !primName.empty() && primName[0] == '/' ) {
            size_t idx = primName.find_last_of("/");
            primName = primName.substr(idx+1);
        } 
    }
    // If the prim path was not explicitly set, try to come up with a reasonable
    // default.
    bool addNumericSuffix = false;
    if( primName.empty() ) {

        int t = gtPrim->getPrimitiveType();
        if( t == GT_PRIM_POINT_MESH || t == GT_PRIM_PARTICLE )
            primName = "points";
        else if( t == GT_PRIM_POLYGON_MESH || t == GT_PRIM_SUBDIVISION_MESH )
            primName = "mesh";
        else if( t == GT_PRIM_CURVE_MESH )
            primName = "curve";
        else if( t == GusdGT_PointInstancer::getStaticPrimitiveType() )
            primName = "instances";
        else if(const char *n = GusdPrimWrapper::getUsdName( t ))
            primName = n;
        else
            primName = "obj";
        
        if( !primName.empty() ) {
            addNumericSuffix = true;
        }
    }

    string primPath = createPrimPath(primName);

    TfToken purpose = UsdGeomTokens->default_;
    {
        GT_Owner own = GT_OWNER_PRIMITIVE;
        GT_DataArrayHandle dah = gtPrim->findAttribute( GUSD_PURPOSE_ATTR, own, 0 );
        if( dah && dah->isValid() ) {
            purpose = TfToken(dah->getS(0));
        }
    }

    if( primType == GT_PRIM_INSTANCE ) {
 
       auto inst = UTverify_cast<const GT_PrimInstance*>(gtPrim.get());
        const GT_PrimitiveHandle geometry = inst->geometry();

        if ( geometry->getPrimitiveType() == GT_GEO_PACKED ) {

            // If we find a packed prim that has a name, this become a group (xform) in 
            // USD. If it doesn't have a name, we just accumulate the transform and recurse.

            auto packedGeo = UTverify_cast<const GT_GEOPrimPacked*>(geometry.get());
            for( GT_Size i = 0; i < inst->transforms()->entries(); ++i ) {

                UT_Matrix4D m;
                inst->transforms()->get(i)->getMatrix(m);

                UT_Matrix4D newCtm = m_localToWorldXform;
                newCtm = m* m_localToWorldXform;

                SdfPath newPath = m_pathPrefix;
                bool recurse = true;

                if( primHasNameAttr || 
                    ( m_forceGroupTopPackedPrim && m_isTopLevel )) {

                    // m_forceGroupTopPackedPrim is used when we are writing instance 
                    // prototypes. We need to add instance id attributes to the top 
                    // level group. Here we make sure that we create that group, even 
                    // if the user hasn't named it.

                    newPath = m_collector.add(  SdfPath(primPath), 
                                                addNumericSuffix,
                                                gtPrim,
                                                newCtm,
                                                purpose,
                                                m_writeCtrlFlags );
            
                    // If we are just writing transforms and encounter a packed prim, we 
                    // just want to write it's transform and not refine it further.
                    recurse = refinePackedPrims;
                }

                if( recurse ) {
                    GusdRefiner childRefiner(
                                    m_collector,
                                    newPath,
                                    m_pathAttrName,
                                    newCtm );
                    
                    childRefiner.m_refinePackedPrims = refinePackedPrims;
                    childRefiner.m_forceGroupTopPackedPrim = m_forceGroupTopPackedPrim;
                    childRefiner.m_isTopLevel = false;

                    childRefiner.m_writeCtrlFlags = m_writeCtrlFlags;
                    childRefiner.m_writeCtrlFlags.update( geometry );

#if UT_MAJOR_VERSION_INT >= 16
                    childRefiner.refineDetail( packedGeo->getPackedDetail(), m_refineParms );
#else
                    childRefiner.refineDetail( packedGeo->getPrim()->getPackedDetail(), m_refineParms );
#endif
                }
            }
            return;
        }
    }

    if( (primType != GT_GEO_PACKED || !refinePackedPrims) && 
                            GusdPrimWrapper::isGTPrimSupported(gtPrim) ) {

        UT_Matrix4D m;
        if( primType == GT_GEO_PACKED ) {
            // packed fragment
            UTverify_cast<const GT_GEOPrimPacked*>(gtPrim.get())->getFullTransform()->getMatrix(m);
        }
        else {
            gtPrim->getPrimitiveTransform()->getMatrix(m);
        }

        UT_Matrix4D newCtm = m_localToWorldXform;
        newCtm = m* m_localToWorldXform;

        m_collector.add( SdfPath(primPath),
                         addNumericSuffix,
                         gtPrim,
                         newCtm,
                         purpose,
                         m_writeCtrlFlags );
    }
    else {
        gtPrim->refine( *this, &m_refineParms );
    }
}