/* 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; }
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; }
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); } } }
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); } } }
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; }
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); } }
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); } } } }
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; }
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 ); } }