bool GusdUSD_StageProxy::MultiAccessor::ClampTimes( UT_Array<UsdTimeCode>& times) const { UT_ASSERT(times.size() == _size); for(exint i = 0; i < times.size(); ++i) { if(const auto* accessor = (*this)(i)) times(i) = accessor->ClampTime(times(i)); } return true; }
bool GusdUSD_StageProxy::MultiAccessor::_GetPrims( const UT_Array<SdfPath>& primPaths, UT_Array<UsdPrim>& prims, GusdUT_ErrorContext* err) { if(!_Load(primPaths)) return false; prims.setSize(primPaths.size()); std::atomic_bool workerInterrupt(false); UTparallelFor(UT_BlockedRange<size_t>(0, primPaths.size()), _GetPrimsFn(*this, primPaths, prims, err, workerInterrupt)); return !UTgetInterrupt()->opInterrupt() && !workerInterrupt; }
int64 GusdBoundsCache::Clear(const UT_Set<std::string>& paths) { int64 freed = 0; UT_Array<Key> keys; for( auto const& entry : m_map ) { if( paths.contains( entry.first.path.GetString() ) ) { keys.append( entry.first ); } } for( auto const& k : keys ) { m_map.erase( k ); } return freed; }
bool GusdUSD_StageProxy::MultiAccessor::Bind( const UT_Array<GusdUSD_StageProxyHandle>& proxies, const UT_Array<SdfPath>& paths, UT_Array<UsdPrim>& prims, UsdStage::InitialLoadSet loadSet, GusdUT_ErrorContext* err) { Release(); UT_ASSERT(paths.isEmpty() || paths.size() == proxies.size()); if(proxies.size() == 0) return true; /* We have an input arrays of proxies and paths. Many of the paths will be associated with the same proxy, but they may also point at different proxies. To avoid having to lock every individual prim, we want to compute a mapping of indices from those input arrays into indices in an array containing just the unique set of proxies.*/ UT_Array<GusdUSD_StageProxyHandle> uniqueProxies; if(!_ComputeUniqueProxies(uniqueProxies, _indexMap, proxies)) return false; _numAccessors = uniqueProxies.size(); _accessors = new Accessor[_numAccessors]; _size = proxies.size(); /* Now the unique set of proxies is known, so acquire accessors (I.e., lock and load stages) */ std::atomic_bool workerInterrupt(false); UTparallelForHeavyItems(UT_BlockedRange<size_t>(0, _numAccessors), _BindAccessorsFn(_accessors, uniqueProxies, loadSet, err, workerInterrupt)); if(UTgetInterrupt()->opInterrupt() || workerInterrupt) return false; /* Any entries referencing proxies that couldn't be bound should have an invalid index; no point in accessing invalid accessors.*/ for(exint i = 0; i < _indexMap.size(); ++i) { int idx = _indexMap(i); if(idx >= 0) { if(!_accessors[idx]) _indexMap(i) = -1; } } return _GetPrims(paths, prims, err); }
void SOP_PrimGroupCentroid::buildGroupData(UT_String &pattern, const GU_Detail *input_geo, UT_Array<GA_Range> &range_array, UT_StringArray &string_values) { GA_Range pr_range; const GA_PrimitiveGroup *group; GA_ElementGroupTable::ordered_iterator group_it; UT_String group_name; UT_WorkArgs tokens; // Tokenize the pattern. pattern.tokenize(tokens); // Get all the primitive groups. const GA_ElementGroupTable &prim_groups = input_geo->primitiveGroups(); // For each primitive group in order. for (group_it=prim_groups.obegin(); !group_it.atEnd(); ++group_it) { // Get the group. group = static_cast<GA_PrimitiveGroup *>(*group_it); // Ensure the group is valid. if (!group) continue; // Skip internal groups. if (group->getInternal()) continue; // Check to see if this group name matches the pattern. group_name = group->getName(); if (!group_name.matchPattern(tokens)) continue; // Get a range for the primitives in the group. pr_range = input_geo->getPrimitiveRange(group); // Add the primitive range and the group name to the arrays. range_array.append(pr_range); string_values.append(group_name); } }
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); } } } }
bool GusdGU_PackedUSD::unpackPrim( GU_Detail& destgdp, UsdGeomImageable prim, const SdfPath& primPath, const UT_Matrix4D& xform, const GT_RefineParms& rparms, bool addPathAttributes ) const { GT_PrimitiveHandle gtPrim = GusdPrimWrapper::defineForRead( prim, m_frame, m_purposes ); if( !gtPrim ) { const TfToken &type = prim.GetPrim().GetTypeName(); if( type != "PxHairman" && type != "PxProcArgs" ) TF_WARN( "Can't convert prim for unpack. %s. Type = %s.", prim.GetPrim().GetPath().GetText(), type.GetText() ); return false; } GusdPrimWrapper* wrapper = UTverify_cast<GusdPrimWrapper*>(gtPrim.get()); if( !wrapper->unpack( destgdp, fileName(), primPath, xform, intrinsicFrame(), #if SYS_VERSION_FULL_INT < 0x10050000 intrinsicViewportLOD(), #else intrinsicViewportLOD( getPrim() ), #endif m_purposes )) { // If the wrapper prim does not do the unpack, do it here. UT_Array<GU_Detail *> details; if( prim.GetPrim().IsInMaster() ) { gtPrim->setPrimitiveTransform( new GT_Transform( &xform, 1 ) ); } GA_Size startIndex = destgdp.getNumPrimitives(); GT_Util::makeGEO(details, gtPrim, &rparms); for (exint i = 0; i < details.entries(); ++i) { copyPrimitiveGroups(*details(i), false); #if SYS_VERSION_FULL_INT < 0x11000000 unpackToDetail(destgdp, details(i), true); #else unpackToDetail(destgdp, details(i), &xform); #endif delete details(i); } if( addPathAttributes ) { // Add usdpath and usdprimpath attributes to unpacked geometry. GA_Size endIndex = destgdp.getNumPrimitives(); const char *path = prim.GetPrim().GetPath().GetString().c_str(); if( endIndex > startIndex ) { GA_RWHandleS primPathAttr( destgdp.addStringTuple( GA_ATTRIB_PRIMITIVE, GUSD_PRIMPATH_ATTR, 1 )); GA_RWHandleS pathAttr( destgdp.addStringTuple( GA_ATTRIB_PRIMITIVE, GUSD_PATH_ATTR, 1 )); for( GA_Size i = startIndex; i < endIndex; ++i ) { primPathAttr.set( destgdp.primitiveOffset( i ), 0, path ); pathAttr.set( destgdp.primitiveOffset( i ), 0, fileName().c_str() ); } } } } return true; }
OP_ERROR GusdSOP_usdimport::_CreateNewPrims(OP_Context& ctx, const GusdUSD_Traverse* traverse, GusdUT_ErrorContext& err) { fpreal t = ctx.getTime(); UT_String file, primPath; evalString(file, "import_file", 0, t); evalString(primPath, "import_primpath", 0, t); if(!file.isstring() || !primPath.isstring()) { // Nothing to do. return UT_ERROR_NONE; } /* The prim path may be a list of prims. Additionally, those prim paths may include variants (eg., /some/model{variant=sel}/subscope ). Including multiple variants may mean that we need to access multiple stages. Resolve the actual set of prims and variants first.*/ UT_Array<SdfPath> primPaths, variants; if(!GusdUSD_Utils::GetPrimAndVariantPathsFromPathList( primPath, primPaths, variants, &err)) return err(); GusdDefaultArray<UT_StringHolder> filePaths; filePaths.SetConstant(file); // Get stage edits applying any of our variants. GusdDefaultArray<GusdStageEditPtr> edits; edits.GetArray().setSize(variants.size()); for(exint i = 0; i < variants.size(); ++i) { if(!variants(i).IsEmpty()) { GusdStageBasicEdit* edit = new GusdStageBasicEdit; edit->GetVariants().append(variants(i)); edits.GetArray()(i).reset(edit); } } // Load the root prims. UT_Array<UsdPrim> rootPrims; { rootPrims.setSize(primPaths.size()); GusdStageCacheReader cache; if(!cache.GetPrims(filePaths, primPaths, edits, rootPrims.data(), GusdStageOpts::LoadAll(), &err)) return err(); } GusdDefaultArray<UsdTimeCode> times; times.SetConstant(evalFloat("import_time", 0, t)); UT_String purpose; evalString(purpose, "purpose", 0, t); GusdDefaultArray<GusdPurposeSet> purposes; purposes.SetConstant(GusdPurposeSet( GusdPurposeSetFromMask(purpose)| GUSD_PURPOSE_DEFAULT)); UT_Array<UsdPrim> prims; if(traverse) { // Before traversal, make a copy of the variants list. const UT_Array<SdfPath> variantsPreTraverse(variants); UT_Array<GusdUSD_Traverse::PrimIndexPair> primIndexPairs; UT_UniquePtr<GusdUSD_Traverse::Opts> opts(traverse->CreateOpts()); if(opts) { if(!opts->Configure(*this, t)) return err(); } if(!traverse->FindPrims(rootPrims, times, purposes, primIndexPairs, /*skip root*/ false, opts.get())) { return err(); } // Resize the prims and variants lists to match the size of // primIndexPairs. exint size = primIndexPairs.size(); prims.setSize(size); variants.setSize(size); // Now iterate through primIndexPairs to populate the prims // and variants lists. for (exint i = 0; i < size; i++) { prims(i) = primIndexPairs(i).first; exint index = primIndexPairs(i).second; variants(i) = index < variantsPreTraverse.size() ? variantsPreTraverse(index) : SdfPath(); } } else { std::swap(prims, rootPrims); } /* Have the resolved set of USD prims. Now create prims or points on the detail.*/ bool packedPrims = !evalInt("import_class", 0, t); if(packedPrims) { UT_String vpLOD; evalString(vpLOD, "viewportlod", 0, t); GusdDefaultArray<UT_StringHolder> lods; lods.SetConstant(vpLOD); GusdGU_USD::AppendPackedPrims(*gdp, prims, variants, times, lods, purposes, &err); } else { GusdGU_USD::AppendRefPoints(*gdp, prims, GUSD_PATH_ATTR, GUSD_PRIMPATH_ATTR, &err); } return err(); }
int SOP_PrimGroupCentroid::buildAttribData(int mode, const GU_Detail *input_geo, UT_Array<GA_Range> &range_array, UT_StringArray &string_values, UT_IntArray &int_values) { int unique_count; exint int_value; GA_Range pr_range; GA_ROAttributeRef source_gah; UT_String attr_name, str_value; // Determine the attribute name to use. attr_name = (mode == 1) ? "name": "class"; // Find the attribute. source_gah = input_geo->findPrimitiveAttribute(attr_name); // If there is no attribute, add an error message and quit. if (source_gah.isInvalid()) { addError(SOP_ATTRIBUTE_INVALID, attr_name); return 1; } // If the 'name' attribute isn't a string, add an error and quit. if (mode == 1 && !source_gah.isString()) { addError(SOP_ATTRIBUTE_INVALID, "'name' must be a string."); return 1; } // If the 'class' attribute isn't an int, add an error and quit. else if (mode == 2 && !source_gah.isInt()) { addError(SOP_ATTRIBUTE_INVALID, "'class' must be an integer."); return 1; } // The number of unique values for the attribute. unique_count = input_geo->getUniqueValueCount(source_gah); // Add all the ranges and unique values to the appropriate arrays. if (mode == 1) { for (int idx=0; idx<unique_count; ++idx) { // Get the unique string value. str_value = input_geo->getUniqueStringValue(source_gah, idx); // Get the primitive range corresponding to that value. pr_range = input_geo->getRangeByValue(source_gah, str_value); // Add the range to the array. range_array.append(pr_range); // Add the string value to the string value list. string_values.append(str_value); } } else { for (int idx=0; idx<unique_count; ++idx) { // Get the unique integer value. int_value = input_geo->getUniqueIntegerValue(source_gah, idx); // Get the primitive range corresponding to that value. pr_range = input_geo->getRangeByValue(source_gah, int_value); // Add the range to the array. range_array.append(pr_range); // Add the integer value to the integer value list. int_values.append(int_value); } } // Return 0 for success. return 0; }
bool GusdUSD_StageProxy::MultiAccessor::_Load(const UT_Array<SdfPath>& paths) { if(paths.isEmpty()) return true; UT_ASSERT_P(paths.size() == _size); typedef tbb::concurrent_unordered_set< SdfPath,SdfPath::Hash> ConcurrentPathSet; struct _ComputeUnloadedPrimsFn { _ComputeUnloadedPrimsFn(const UT_Array<SdfPath>& paths, const UT_Array<exint>& indexMap, const UT_Array<_PrimLoader*>& loaders, UT_Array<ConcurrentPathSet*>& pathSets) : _paths(paths), _indexMap(indexMap), _loaders(loaders), _pathSets(pathSets) {} void operator()(const UT_BlockedRange<size_t>& r) const { auto* boss = UTgetInterrupt(); char bcnt = 0; for(size_t i = r.begin(); i < r.end(); ++i) { if(BOOST_UNLIKELY(!++bcnt && boss->opInterrupt())) return; exint idx = _indexMap(i); if(idx >= 0) { if(auto* loader = _loaders(idx)) { const SdfPath& path = _paths(i); if(!path.IsEmpty() && !loader->IsLoaded(path)) _pathSets(idx)->insert(path); } } } } private: const UT_Array<SdfPath>& _paths; const UT_Array<exint>& _indexMap; const UT_Array<_PrimLoader*>& _loaders; UT_Array<ConcurrentPathSet*> _pathSets; }; /* Compute sets of unloaded prims. This constitutes the bulk of binding time, so do this parallel.*/ UT_Array<_PrimLoader*> loaders(_numAccessors, _numAccessors); UT_Array<ConcurrentPathSet*> pathSets(_numAccessors, _numAccessors); for(exint i = 0; i < _numAccessors; ++i) { if ( _accessors[i] ) { auto* loader = _accessors[i].GetProxy()->_primLoader; if(loader) { loaders(i) = loader; pathSets(i) = new ConcurrentPathSet; } } } UTparallelFor(UT_BlockedRange<size_t>(0, _size), _ComputeUnloadedPrimsFn(paths, _indexMap, loaders, pathSets)); if(UTgetInterrupt()->opInterrupt()) return false; /* Load the actual prims. This could be done in parallel, but is probably not worth it since there's only work to perform the first time a prim load is requested.*/ for(exint i = 0; i < _numAccessors; ++i) { if(auto* pathSet = pathSets(i)) { SdfPathSet pathsToLoad(pathSet->begin(), pathSet->end()); _accessors[i]._Load(pathsToLoad); delete pathSet; } } return true; }