bool UsdGeomCollectionAPI::AppendTarget( const SdfPath &target, const VtIntArray &faceIndices /* =VtIntArray() */, const UsdTimeCode &time /* =UsdTimeCode::Default() */) const { if (target.IsEmpty()) { TF_CODING_ERROR("Cannot add empty target to collection '%s' on " "prim <%s>.", _name.GetText(), GetPath().GetText()); return false; } bool hasFaceCountsAtTime = true; if (time != UsdTimeCode::Default()) { UsdAttribute targetFaceCountsAttr = GetTargetFaceCountsAttr(); double lower=0., upper=0.; bool hasTimeSamples=false; if (targetFaceCountsAttr.GetBracketingTimeSamples(time.GetValue(), &lower, &upper, &hasTimeSamples)) { hasFaceCountsAtTime = (lower==upper && lower==time.GetValue()); } } VtIntArray targetFaceCounts, targetFaceIndices; if (hasFaceCountsAtTime) { GetTargetFaceCounts(&targetFaceCounts, time); GetTargetFaceIndices(&targetFaceIndices, time); } SdfPathVector targets; GetTargets(&targets); // If there are no existing face restrictions and no face-restriction is // specified on the current target, simly add the target and return. if (targetFaceCounts.empty() && targetFaceIndices.empty() && faceIndices.empty()) { // We can simply add the target here to the relationship here since // there are no companion non-list-edited integer arrays. return CreateTargetsRel().AppendTarget(target); } if (targetFaceCounts.empty() && !targetFaceIndices.empty()) { TF_CODING_ERROR("targetFaceCounts is empty, but targetFaceIndices " "is not, for the collection '%s' belonging to prim <%s>.", _name.GetText(), GetPath().GetText()); return false; } if (targetFaceCounts.empty() && !faceIndices.empty()) { for (size_t i = 0 ; i < targets.size(); i++) targetFaceCounts.push_back(0); } targetFaceCounts.push_back(faceIndices.size()); targetFaceIndices.reserve(targetFaceIndices.size() + faceIndices.size()); TF_FOR_ALL(it, faceIndices) { targetFaceIndices.push_back(*it); }
bool UsdRelationship::SetTargets(const SdfPathVector& targets) const { SdfPathVector mappedPaths; mappedPaths.reserve(targets.size()); for (const SdfPath &target: targets) { std::string errMsg; mappedPaths.push_back(_GetTargetForAuthoring(target, &errMsg)); if (mappedPaths.back().IsEmpty()) { TF_CODING_ERROR("Cannot set target <%s> on relationship <%s>: %s", target.GetText(), GetPath().GetText(), errMsg.c_str()); return false; } } // NOTE! Do not insert any code that modifies scene description between the // changeblock and the call to _CreateSpec! Explanation: _CreateSpec calls // code that inspects the composition graph and then does some authoring. // We want that authoring to be inside the change block, but if any scene // description changes are made after the block is created but before we // call _CreateSpec, the composition structure may be invalidated. SdfChangeBlock block; SdfRelationshipSpecHandle relSpec = _CreateSpec(); if (!relSpec) return false; relSpec->GetTargetPathList().ClearEditsAndMakeExplicit(); for (const SdfPath &path: mappedPaths) { relSpec->GetTargetPathList().Add(path); } return true; }
UsdVolVolume::FieldMap UsdVolVolume::GetFieldPaths() const { std::map<TfToken, SdfPath> fieldMap; const UsdPrim &prim = GetPrim(); if (prim) { std::vector<UsdProperty> fieldProps = prim.GetPropertiesInNamespace(_tokens->fieldPrefix); for (const UsdProperty &fieldProp : fieldProps) { UsdRelationship fieldRel = fieldProp.As<UsdRelationship>(); SdfPathVector targets; // All relationships starting with "field:" should point to // UsdVolFieldBase primitives. if (fieldRel && fieldRel.GetForwardedTargets(&targets)) { if (targets.size() == 1 && targets.front().IsPrimPath()) { fieldMap.emplace(fieldRel.GetBaseName(), targets.front()); } } } } return fieldMap; }
PXR_NAMESPACE_OPEN_SCOPE // TODO: We should centralize this logic in a UsdImaging ShaderAdapter. /*static*/ UsdPrim UsdImaging_MaterialStrategy::GetTargetedShader(UsdPrim const& materialPrim, UsdRelationship const& materialRel) { SdfPathVector targets; if (!materialRel.GetForwardedTargets(&targets)) return UsdPrim(); if (targets.size() != 1) { // XXX: This should really be a validation error once USD gets that // feature. TF_WARN("We expect only one target on relationship %s of prim <%s>, " "but got %zu.", materialRel.GetName().GetText(), materialPrim.GetPath().GetText(), targets.size()); return UsdPrim(); } if (!targets[0].IsPrimPath()) { // XXX: This should really be a validation error once USD gets that // feature. TF_WARN("We expect the target of the relationship %s of prim <%s> " "to be a prim, instead it is <%s>.", materialRel.GetName().GetText(), materialPrim.GetPath().GetText(), targets[0].GetText()); return UsdPrim(); } return materialPrim.GetStage()->GetPrimAtPath(targets[0]); }
bool UsdRelationship::SetTargets(const SdfPathVector& targets) const { SdfPathVector mappedPaths; mappedPaths.reserve(targets.size()); BOOST_FOREACH(const SdfPath &target, targets) { std::string errMsg; mappedPaths.push_back(_GetTargetForAuthoring(target, &errMsg)); if (mappedPaths.back().IsEmpty()) { TF_CODING_ERROR("Cannot set target <%s> on relationship <%s>: %s", target.GetText(), GetPath().GetText(), errMsg.c_str()); return false; } }
SdfPath UsdVolVolume::GetFieldPath(const TfToken &name) const { UsdRelationship fieldRel = GetPrim().GetRelationship(_MakeNamespaced(name)); SdfPathVector targets; if (fieldRel && fieldRel.GetForwardedTargets(&targets)) { if (targets.size() == 1 && targets.front().IsPrimPath()) { return targets.front(); } } return SdfPath::EmptyPath(); }
bool UsdGeomPrimvar::Get( std::string* value, UsdTimeCode time) const { // check if there is a relationship and if so use the target path string to // get the string value. if (!_idTargetRelName.IsEmpty()) { if (UsdRelationship rel = _GetIdTargetRel(false)) { SdfPathVector targets; if (rel.GetForwardedTargets(&targets) && targets.size() == 1) { *value = targets[0].GetString(); return true; } return false; } } return _attr.Get(value, time); }
bool UsdGeomPrimvar::Get( VtStringArray* value, UsdTimeCode time) const { // check if there is a relationship and if so use the target path string to // get the string value... Just take the first target, for now. if (!_idTargetRelName.IsEmpty()) { if (UsdRelationship rel = _GetIdTargetRel(false)) { value->clear(); SdfPathVector targets; if (rel.GetForwardedTargets(&targets) && targets.size() > 1) { value->push_back(targets[0].GetString()); return true; } return false; } } return _attr.Get(value, time); }
void PxrUsdKatanaReadPointInstancer( const UsdGeomPointInstancer& instancer, const PxrUsdKatanaUsdInPrivateData& data, PxrUsdKatanaAttrMap& instancerAttrMap, PxrUsdKatanaAttrMap& sourcesAttrMap, PxrUsdKatanaAttrMap& instancesAttrMap, PxrUsdKatanaAttrMap& inputAttrMap) { const double currentTime = data.GetCurrentTime(); PxrUsdKatanaReadXformable(instancer, data, instancerAttrMap); // Get primvars for setting later. Unfortunatley, the only way to get them // out of the attr map is to build it, which will cause its contents to be // cleared. We'll need to restore its contents before continuing. // FnKat::GroupAttribute instancerAttrs = instancerAttrMap.build(); FnKat::GroupAttribute primvarAttrs = instancerAttrs.getChildByName("geometry.arbitrary"); for (int64_t i = 0; i < instancerAttrs.getNumberOfChildren(); ++i) { instancerAttrMap.set(instancerAttrs.getChildName(i), instancerAttrs.getChildByIndex(i)); } instancerAttrMap.set("type", FnKat::StringAttribute("usd point instancer")); const std::string fileName = data.GetUsdInArgs()->GetFileName(); instancerAttrMap.set("info.usd.fileName", FnKat::StringAttribute(fileName)); FnKat::GroupAttribute inputAttrs = inputAttrMap.build(); const std::string katOutputPath = FnKat::StringAttribute( inputAttrs.getChildByName("outputLocationPath")).getValue("", false); if (katOutputPath.empty()) { _LogAndSetError(instancerAttrMap, "No output location path specified"); return; } // // Validate instancer data. // const std::string instancerPath = instancer.GetPath().GetString(); UsdStageWeakPtr stage = instancer.GetPrim().GetStage(); // Prototypes (required) // SdfPathVector protoPaths; instancer.GetPrototypesRel().GetTargets(&protoPaths); if (protoPaths.empty()) { _LogAndSetError(instancerAttrMap, "Instancer has no prototypes"); return; } _PathToPrimMap primCache; for (auto protoPath : protoPaths) { const UsdPrim &protoPrim = stage->GetPrimAtPath(protoPath); primCache[protoPath] = protoPrim; } // Indices (required) // VtIntArray protoIndices; if (!instancer.GetProtoIndicesAttr().Get(&protoIndices, currentTime)) { _LogAndSetError(instancerAttrMap, "Instancer has no prototype indices"); return; } const size_t numInstances = protoIndices.size(); if (numInstances == 0) { _LogAndSetError(instancerAttrMap, "Instancer has no prototype indices"); return; } for (auto protoIndex : protoIndices) { if (protoIndex < 0 || static_cast<size_t>(protoIndex) >= protoPaths.size()) { _LogAndSetError(instancerAttrMap, TfStringPrintf( "Out of range prototype index %d", protoIndex)); return; } } // Mask (optional) // std::vector<bool> pruneMaskValues = instancer.ComputeMaskAtTime(currentTime); if (!pruneMaskValues.empty() and pruneMaskValues.size() != numInstances) { _LogAndSetError(instancerAttrMap, "Mismatch in length of indices and mask"); return; } // Positions (required) // UsdAttribute positionsAttr = instancer.GetPositionsAttr(); if (!positionsAttr.HasValue()) { _LogAndSetError(instancerAttrMap, "Instancer has no positions"); return; } // // Compute instance transform matrices. // const double timeCodesPerSecond = stage->GetTimeCodesPerSecond(); // Gather frame-relative sample times and add them to the current time to // generate absolute sample times. // const std::vector<double> &motionSampleTimes = data.GetMotionSampleTimes(positionsAttr); const size_t sampleCount = motionSampleTimes.size(); std::vector<UsdTimeCode> sampleTimes(sampleCount); for (size_t a = 0; a < sampleCount; ++a) { sampleTimes[a] = UsdTimeCode(currentTime + motionSampleTimes[a]); } // Get velocityScale from the opArgs. // float velocityScale = FnKat::FloatAttribute( inputAttrs.getChildByName("opArgs.velocityScale")).getValue(1.0f, false); // XXX Replace with UsdGeomPointInstancer::ComputeInstanceTransformsAtTime. // std::vector<std::vector<GfMatrix4d>> xformSamples(sampleCount); const size_t numXformSamples = _ComputeInstanceTransformsAtTime(xformSamples, instancer, sampleTimes, UsdTimeCode(currentTime), timeCodesPerSecond, numInstances, positionsAttr, velocityScale); if (numXformSamples == 0) { _LogAndSetError(instancerAttrMap, "Could not compute " "sample/topology-invarying instance " "transform matrix"); return; } // // Compute prototype bounds. // bool aggregateBoundsValid = false; std::vector<double> aggregateBounds; // XXX Replace with UsdGeomPointInstancer::ComputeExtentAtTime. // VtVec3fArray aggregateExtent; if (_ComputeExtentAtTime( aggregateExtent, data.GetUsdInArgs(), xformSamples, motionSampleTimes, protoIndices, protoPaths, primCache, pruneMaskValues)) { aggregateBoundsValid = true; aggregateBounds.resize(6); aggregateBounds[0] = aggregateExtent[0][0]; // min x aggregateBounds[1] = aggregateExtent[1][0]; // max x aggregateBounds[2] = aggregateExtent[0][1]; // min y aggregateBounds[3] = aggregateExtent[1][1]; // max y aggregateBounds[4] = aggregateExtent[0][2]; // min z aggregateBounds[5] = aggregateExtent[1][2]; // max z } // // Build sources. Keep track of which instances use them. // FnGeolibServices::StaticSceneCreateOpArgsBuilder sourcesBldr(false); std::vector<int> instanceIndices; instanceIndices.reserve(numInstances); std::vector<std::string> instanceSources; instanceSources.reserve(protoPaths.size()); std::map<std::string, int> instanceSourceIndexMap; std::vector<int> omitList; omitList.reserve(numInstances); std::map<SdfPath, std::string> protoPathsToKatPaths; for (size_t i = 0; i < numInstances; ++i) { int index = protoIndices[i]; // Check to see if we are pruned. // bool isPruned = (!pruneMaskValues.empty() and pruneMaskValues[i] == false); if (isPruned) { omitList.push_back(i); } const SdfPath &protoPath = protoPaths[index]; // Compute the full (Katana) path to this prototype. // std::string fullProtoPath; std::map<SdfPath, std::string>::const_iterator pptkpIt = protoPathsToKatPaths.find(protoPath); if (pptkpIt != protoPathsToKatPaths.end()) { fullProtoPath = pptkpIt->second; } else { _PathToPrimMap::const_iterator pcIt = primCache.find(protoPath); const UsdPrim &protoPrim = pcIt->second; if (!protoPrim) { continue; } // Determine where (what path) to start building the prototype prim // such that its material bindings will be preserved. This could be // the prototype path itself or an ancestor path. // SdfPathVector commonPrefixes; UsdRelationship materialBindingsRel = UsdShadeMaterial::GetBindingRel(protoPrim); auto assetAPI = UsdModelAPI(protoPrim); std::string assetName; bool isReferencedModelPrim = assetAPI.IsModel() and assetAPI.GetAssetName(&assetName); if (!materialBindingsRel or isReferencedModelPrim) { // The prim has no material bindings or is a referenced model // prim (meaning that materials are defined below it); start // building at the prototype path. // commonPrefixes.push_back(protoPath); } else { SdfPathVector materialPaths; materialBindingsRel.GetForwardedTargets(&materialPaths); for (auto materialPath : materialPaths) { const SdfPath &commonPrefix = protoPath.GetCommonPrefix(materialPath); if (commonPrefix.GetString() == "/") { // XXX Unhandled case. // The prototype prim and its material are not under the // same parent; start building at the prototype path // (although it is likely that bindings will be broken). // commonPrefixes.push_back(protoPath); } else { // Start building at the common ancestor between the // prototype prim and its material. // commonPrefixes.push_back(commonPrefix); } } } // XXX Unhandled case. // We'll use the first common ancestor even if there is more than // one (which shouldn't appen if the prototype prim and its bindings // are under the same parent). // SdfPath::RemoveDescendentPaths(&commonPrefixes); const std::string buildPath = commonPrefixes[0].GetString(); // See if the path is a child of the point instancer. If so, we'll // match its hierarchy. If not, we'll put it under a 'prototypes' // group. // std::string relBuildPath; if (pystring::startswith(buildPath, instancerPath + "/")) { relBuildPath = pystring::replace( buildPath, instancerPath + "/", ""); } else { relBuildPath = "prototypes/" + FnGeolibUtil::Path::GetLeafName(buildPath); } // Start generating the full path to the prototype. // fullProtoPath = katOutputPath + "/" + relBuildPath; // Make the common ancestor our instance source. // sourcesBldr.setAttrAtLocation(relBuildPath, "type", FnKat::StringAttribute("instance source")); // Author a tracking attr. // sourcesBldr.setAttrAtLocation(relBuildPath, "info.usd.sourceUsdPath", FnKat::StringAttribute(buildPath)); // Tell the BuildIntermediate op to start building at the common // ancestor. // sourcesBldr.setAttrAtLocation(relBuildPath, "usdPrimPath", FnKat::StringAttribute(buildPath)); sourcesBldr.setAttrAtLocation(relBuildPath, "usdPrimName", FnKat::StringAttribute("geo")); if (protoPath.GetString() != buildPath) { // Finish generating the full path to the prototype. // fullProtoPath = fullProtoPath + "/geo" + pystring::replace( protoPath.GetString(), buildPath, ""); } // Create a mapping that will link the instance's index to its // prototype's full path. // instanceSourceIndexMap[fullProtoPath] = instanceSources.size(); instanceSources.push_back(fullProtoPath); // Finally, store the full path in the map so we won't have to do // this work again. // protoPathsToKatPaths[protoPath] = fullProtoPath; } instanceIndices.push_back(instanceSourceIndexMap[fullProtoPath]); } // // Build instances. // FnGeolibServices::StaticSceneCreateOpArgsBuilder instancesBldr(false); instancesBldr.createEmptyLocation("instances", "instance array"); instancesBldr.setAttrAtLocation("instances", "geometry.instanceSource", FnKat::StringAttribute(instanceSources, 1)); instancesBldr.setAttrAtLocation("instances", "geometry.instanceIndex", FnKat::IntAttribute(&instanceIndices[0], instanceIndices.size(), 1)); FnKat::DoubleBuilder instanceMatrixBldr(16); for (size_t a = 0; a < numXformSamples; ++a) { double relSampleTime = motionSampleTimes[a]; // Shove samples into the builder at the frame-relative sample time. If // motion is backwards, make sure to reverse time samples. std::vector<double> &matVec = instanceMatrixBldr.get( data.IsMotionBackward() ? PxrUsdKatanaUtils::ReverseTimeSample(relSampleTime) : relSampleTime); matVec.reserve(16 * numInstances); for (size_t i = 0; i < numInstances; ++i) { GfMatrix4d instanceXform = xformSamples[a][i]; const double *matArray = instanceXform.GetArray(); for (int j = 0; j < 16; ++j) { matVec.push_back(matArray[j]); } } } instancesBldr.setAttrAtLocation("instances", "geometry.instanceMatrix", instanceMatrixBldr.build()); if (!omitList.empty()) { instancesBldr.setAttrAtLocation("instances", "geometry.omitList", FnKat::IntAttribute(&omitList[0], omitList.size(), 1)); } instancesBldr.setAttrAtLocation("instances", "geometry.pointInstancerId", FnKat::StringAttribute(katOutputPath)); // // Transfer primvars. // FnKat::GroupBuilder instancerPrimvarsBldr; FnKat::GroupBuilder instancesPrimvarsBldr; for (int64_t i = 0; i < primvarAttrs.getNumberOfChildren(); ++i) { const std::string primvarName = primvarAttrs.getChildName(i); // Use "point" scope for the instancer. instancerPrimvarsBldr.set(primvarName, primvarAttrs.getChildByIndex(i)); instancerPrimvarsBldr.set(primvarName + ".scope", FnKat::StringAttribute("point")); // User "primitive" scope for the instances. instancesPrimvarsBldr.set(primvarName, primvarAttrs.getChildByIndex(i)); instancesPrimvarsBldr.set(primvarName + ".scope", FnKat::StringAttribute("primitive")); } instancerAttrMap.set("geometry.arbitrary", instancerPrimvarsBldr.build()); instancesBldr.setAttrAtLocation("instances", "geometry.arbitrary", instancesPrimvarsBldr.build()); // // Set the final aggregate bounds. // if (aggregateBoundsValid) { instancerAttrMap.set("bound", FnKat::DoubleAttribute(&aggregateBounds[0], 6, 2)); } // // Set proxy attrs. // instancerAttrMap.set("proxies", PxrUsdKatanaUtils::GetViewerProxyAttr(data)); // // Transfer builder results to our attr maps. // FnKat::GroupAttribute sourcesAttrs = sourcesBldr.build(); for (int64_t i = 0; i < sourcesAttrs.getNumberOfChildren(); ++i) { sourcesAttrMap.set( sourcesAttrs.getChildName(i), sourcesAttrs.getChildByIndex(i)); } FnKat::GroupAttribute instancesAttrs = instancesBldr.build(); for (int64_t i = 0; i < instancesAttrs.getNumberOfChildren(); ++i) { instancesAttrMap.set( instancesAttrs.getChildName(i), instancesAttrs.getChildByIndex(i)); } }
bool HdDirtyList::ApplyEdit(HdRprimCollection const& col) { HD_TRACE_FUNCTION(); // Don't attempt to transition dirty lists where the collection // fundamentally changed, we can't reused filtered paths in those cases. // // when repr changes, don't reuse the dirty list, since the required // DirtyBits may change. if (col.GetName() != _collection.GetName() || col.GetReprName() != _collection.GetReprName() || col.IsForcedRepr() != _collection.IsForcedRepr() || col.GetRenderTags() != _collection.GetRenderTags()) { return false; } // Also don't attempt to fix-up dirty lists when the collection is radically // different in terms of root paths; here a heuristic of 100 root paths is // used as a threshold for when we will stop attempting to fix the list. if (std::abs(int(col.GetRootPaths().size()) - int(_collection.GetRootPaths().size())) > 100) { return false; } // If the either the old or new collection has Exclude paths do // the full rebuild. if (!col.GetExcludePaths().empty() || !_collection.GetExcludePaths().empty()) { return false; } // If the varying state has changed - Rebuild the base list // before adding the new items HdChangeTracker &changeTracker = _renderIndex.GetChangeTracker(); unsigned int currentVaryingStateVersion = changeTracker.GetVaryingStateVersion(); if (_varyingStateVersion != currentVaryingStateVersion) { TF_DEBUG(HD_DIRTY_LIST).Msg("DirtyList(%p): varying state changed " "(%s, %d -> %d)\n", (void*)this, _collection.GetName().GetText(), _varyingStateVersion, currentVaryingStateVersion); // populate only varying prims in the collection _UpdateIDs(&_dirtyIds, HdChangeTracker::Varying); _varyingStateVersion = currentVaryingStateVersion; } SdfPathVector added, removed; typedef SdfPathVector::const_iterator ITR; ITR newI = col.GetRootPaths().cbegin(); ITR newEnd = col.GetRootPaths().cend(); ITR oldI = _collection.GetRootPaths().cbegin(); ITR oldEnd = _collection.GetRootPaths().cend(); HdRenderIndex& index = _renderIndex; TfToken const & repr = col.GetReprName(); TF_DEBUG(HD_DIRTY_LIST).Msg("DirtyList(%p): ApplyEdit\n", (void*)this); if (TfDebug::IsEnabled(HD_DIRTY_LIST)) { std::cout << " Old Collection: " << std::endl; for (auto const& i : _collection.GetRootPaths()) { std::cout << " " << i << std::endl; } std::cout << " Old _dirtyIds: " << std::endl; for (auto const& i : _dirtyIds) { std::cout << " " << i << std::endl; } } const SdfPathVector &paths = _renderIndex.GetRprimIds(); while (newI != newEnd || oldI != oldEnd) { if (newI != newEnd && oldI != oldEnd && *newI == *oldI) { ++newI; ++oldI; continue; } // If any paths in the two sets are prefixed by one another, the logic // below doesn't work, since the subtree has to be fixed up (it's not // just a simple prefix scan). In these cases, we'll just rebuild the // entire list. if (newI != newEnd && oldI != oldEnd && newI->HasPrefix(*oldI)) { return false; } if (newI != newEnd && oldI != oldEnd && oldI->HasPrefix(*newI)) { return false; } if (newI != newEnd && (oldI == oldEnd || *newI < *oldI)) { HdPrimGather gather; SdfPathVector newPaths; gather.Subtree(paths, *newI, &newPaths); size_t numNewPaths = newPaths.size(); for (size_t newPathNum = 0; newPathNum < numNewPaths; ++newPathNum) { const SdfPath &newPath = newPaths[newPathNum]; if (col.HasRenderTag(index.GetRenderTag(newPath, repr))) { _dirtyIds.push_back(newPath); changeTracker.MarkRprimDirty(newPath, HdChangeTracker::InitRepr); } } ++newI; } else if (oldI != oldEnd) { // oldI < newI: Item removed in new list SdfPath const& oldPath = *oldI; _dirtyIds.erase(std::remove_if(_dirtyIds.begin(), _dirtyIds.end(), [&oldPath](SdfPath const& p){return p.HasPrefix(oldPath);}), _dirtyIds.end()); ++oldI; } } _collection = col; _collectionVersion = changeTracker.GetCollectionVersion(_collection.GetName()); // make sure the next GetDirtyRprims() picks up the updated list. _isEmpty = false; if (TfDebug::IsEnabled(HD_DIRTY_LIST)) { std::cout << " New Collection: " << std::endl; for (auto const& i : _collection.GetRootPaths()) { std::cout << " " << i << std::endl; } std::cout << " New _dirtyIds: " << std::endl; for (auto const& i : _dirtyIds) { std::cout << " " << i << std::endl; } } return true; }
static FnKat::Attribute _GetMaterialAssignAttr( const UsdPrim& prim, const PxrUsdKatanaUsdInPrivateData& data) { if (not prim or prim.GetPath() == SdfPath::AbsoluteRootPath()) { // Special-case to pre-empt coding errors. return FnKat::Attribute(); } UsdRelationship usdRel = UsdShadeLook::GetBindingRel(prim); if (usdRel) { // USD shading binding SdfPathVector targetPaths; usdRel.GetForwardedTargets(&targetPaths); if (targetPaths.size() > 0) { if (not targetPaths[0].IsPrimPath()) { FnLogWarn("Target path " << prim.GetPath().GetString() << " is not a prim"); return FnKat::Attribute(); } // This is a copy as it could be modified below. SdfPath targetPath = targetPaths[0]; UsdPrim targetPrim = data.GetUsdInArgs()->GetStage()->GetPrimAtPath(targetPath); // If the target is inside a master, then it needs to be re-targeted // to the instance. // // XXX remove this special awareness once GetMasterWithContext is // is available as the provided prim will automatically // retarget (or provide enough context to retarget without // tracking manually). if (targetPrim and targetPrim.IsInMaster()) { if (not data.GetInstancePath().IsEmpty() and not data.GetMasterPath().IsEmpty()) { // Check if the source and the target of the relationship // belong to the same master. // If they do, we have the context necessary to do the // re-mapping. if (data.GetMasterPath().GetCommonPrefix(targetPath). GetPathElementCount() > 0) { targetPath = data.GetInstancePath().AppendPath( targetPath.ReplacePrefix(targetPath.GetPrefixes()[0], SdfPath::ReflexiveRelativePath())); } else { // Warn saying the target of relationship isn't within // the same master as the source. FnLogWarn("Target path " << prim.GetPath().GetString() << " isn't within the master " << data.GetMasterPath()); return FnKat::Attribute(); } } else { // XXX // When loading beneath a master via an isolatePath // opArg, we can encounter targets which are within masters // but not within the context of a material. // While that would be an error according to the below // warning, it produces the expected results. // This case can occur when expanding pointinstancers as // the sources are made via execution of PxrUsdIn again // at the sub-trees. // Warn saying target of relationship is in a master, // but the associated instance path is unknown! // FnLogWarn("Target path " << prim.GetPath().GetString() // << " is within a master, but the associated " // "instancePath is unknown."); // return FnKat::Attribute(); } } // Convert the target path to the equivalent katana location. // XXX: Looks may have an atypical USD->Katana // path mapping std::string location = PxrUsdKatanaUtils::ConvertUsdLookPathToKatLocation(targetPath, data); // XXX Looks containing only display terminals are causing issues // with katana material manipulation workflows. // For now: exclude any material assign which doesn't include // /Looks/ in the path if (location.find(UsdKatanaTokens->katanaLooksScopePathSubstring) == std::string::npos) { return FnKat::Attribute(); } // location = TfStringReplace(location, "/Looks/", "/Materials/"); // XXX handle multiple assignments return FnKat::StringAttribute(location); } } return FnKat::Attribute(); }
bool UsdGeomPointInstancer::ComputeExtentAtTime( VtVec3fArray* extent, const UsdTimeCode time, const UsdTimeCode baseTime) const { if (!extent) { TF_WARN("%s -- null container passed to ComputeExtentAtTime()", GetPrim().GetPath().GetText()); return false; } VtIntArray protoIndices; if (!GetProtoIndicesAttr().Get(&protoIndices, time)) { TF_WARN("%s -- no prototype indices", GetPrim().GetPath().GetText()); return false; } const std::vector<bool> mask = ComputeMaskAtTime(time); if (!mask.empty() && mask.size() != protoIndices.size()) { TF_WARN("%s -- mask.size() [%zu] != protoIndices.size() [%zu]", GetPrim().GetPath().GetText(), mask.size(), protoIndices.size()); return false; } const UsdRelationship prototypes = GetPrototypesRel(); SdfPathVector protoPaths; if (!prototypes.GetTargets(&protoPaths) || protoPaths.empty()) { TF_WARN("%s -- no prototypes", GetPrim().GetPath().GetText()); return false; } // verify that all the protoIndices are in bounds. TF_FOR_ALL(iter, protoIndices) { const int protoIndex = *iter; if (protoIndex < 0 || static_cast<size_t>(protoIndex) >= protoPaths.size()) { TF_WARN("%s -- invalid prototype index: %d. Should be in [0, %zu)", GetPrim().GetPath().GetText(), protoIndex, protoPaths.size()); return false; } } // Note that we do NOT apply any masking when computing the instance // transforms. This is so that for a particular instance we can determine // both its transform and its prototype. Otherwise, the instanceTransforms // array would have masked instances culled out and we would lose the // mapping to the prototypes. // Masked instances will be culled before being applied to the extent below. VtMatrix4dArray instanceTransforms; if (!ComputeInstanceTransformsAtTime(&instanceTransforms, time, baseTime, IncludeProtoXform, IgnoreMask)) { TF_WARN("%s -- could not compute instance transforms", GetPrim().GetPath().GetText()); return false; } UsdStageWeakPtr stage = GetPrim().GetStage(); const TfTokenVector purposes { UsdGeomTokens->default_, UsdGeomTokens->proxy, UsdGeomTokens->render }; UsdGeomBBoxCache bboxCache(time, purposes); bboxCache.SetTime(time); GfRange3d extentRange; for (size_t instanceId = 0; instanceId < protoIndices.size(); ++instanceId) { if (!mask.empty() && !mask[instanceId]) { continue; } const int protoIndex = protoIndices[instanceId]; const SdfPath& protoPath = protoPaths[protoIndex]; const UsdPrim& protoPrim = stage->GetPrimAtPath(protoPath); // Get the prototype bounding box. GfBBox3d thisBounds = bboxCache.ComputeUntransformedBound(protoPrim); // Apply the instance transform. thisBounds.Transform(instanceTransforms[instanceId]); extentRange.UnionWith(thisBounds.ComputeAlignedRange()); } const GfVec3d extentMin = extentRange.GetMin(); const GfVec3d extentMax = extentRange.GetMax(); *extent = VtVec3fArray(2); (*extent)[0] = GfVec3f(extentMin[0], extentMin[1], extentMin[2]); (*extent)[1] = GfVec3f(extentMax[0], extentMax[1], extentMax[2]); return true; }
bool UsdGeomPointInstancer::ComputeInstanceTransformsAtTime( VtArray<GfMatrix4d>* xforms, const UsdTimeCode time, const UsdTimeCode baseTime, const ProtoXformInclusion doProtoXforms, const MaskApplication applyMask) const { // XXX: Need to add handling of velocities/angularVelocities and baseTime. (void)baseTime; if (!xforms) { TF_WARN("%s -- null container passed to ComputeInstanceTransformsAtTime()", GetPrim().GetPath().GetText()); return false; } VtIntArray protoIndices; if (!GetProtoIndicesAttr().Get(&protoIndices, time)) { TF_WARN("%s -- no prototype indices", GetPrim().GetPath().GetText()); return false; } if (protoIndices.empty()) { xforms->clear(); return true; } VtVec3fArray positions; if (!GetPositionsAttr().Get(&positions, time)) { TF_WARN("%s -- no positions", GetPrim().GetPath().GetText()); return false; } if (positions.size() != protoIndices.size()) { TF_WARN("%s -- positions.size() [%zu] != protoIndices.size() [%zu]", GetPrim().GetPath().GetText(), positions.size(), protoIndices.size()); return false; } VtVec3fArray scales; GetScalesAttr().Get(&scales, time); if (!scales.empty() && scales.size() != protoIndices.size()) { TF_WARN("%s -- scales.size() [%zu] != protoIndices.size() [%zu]", GetPrim().GetPath().GetText(), scales.size(), protoIndices.size()); return false; } VtQuathArray orientations; GetOrientationsAttr().Get(&orientations, time); if (!orientations.empty() && orientations.size() != protoIndices.size()) { TF_WARN("%s -- orientations.size() [%zu] != protoIndices.size() [%zu]", GetPrim().GetPath().GetText(), orientations.size(), protoIndices.size()); return false; } // If we're going to include the prototype transforms, verify that we have // prototypes and that all of the protoIndices are in bounds. SdfPathVector protoPaths; if (doProtoXforms == IncludeProtoXform) { const UsdRelationship prototypes = GetPrototypesRel(); if (!prototypes.GetTargets(&protoPaths) || protoPaths.empty()) { TF_WARN("%s -- no prototypes", GetPrim().GetPath().GetText()); return false; } TF_FOR_ALL(iter, protoIndices) { const int protoIndex = *iter; if (protoIndex < 0 || static_cast<size_t>(protoIndex) >= protoPaths.size()) { TF_WARN("%s -- invalid prototype index: %d. Should be in [0, %zu)", GetPrim().GetPath().GetText(), protoIndex, protoPaths.size()); return false; } } } // Compute the mask only if applyMask says we should, otherwise we leave // mask empty so that its application below is a no-op. std::vector<bool> mask; if (applyMask == ApplyMask) { mask = ComputeMaskAtTime(time); if (!mask.empty() && mask.size() != protoIndices.size()) { TF_WARN("%s -- mask.size() [%zu] != protoIndices.size() [%zu]", GetPrim().GetPath().GetText(), mask.size(), protoIndices.size()); return false; } } UsdStageWeakPtr stage = GetPrim().GetStage(); UsdGeomXformCache xformCache(time); xforms->assign(protoIndices.size(), GfMatrix4d(1.0)); for (size_t instanceId = 0; instanceId < protoIndices.size(); ++instanceId) { if (!mask.empty() && !mask[instanceId]) { continue; } GfTransform instanceTransform; if (!scales.empty()) { instanceTransform.SetScale(scales[instanceId]); } if (!orientations.empty()) { instanceTransform.SetRotation(GfRotation(orientations[instanceId])); } instanceTransform.SetTranslation(positions[instanceId]); GfMatrix4d protoXform(1.0); if (doProtoXforms == IncludeProtoXform) { const int protoIndex = protoIndices[instanceId]; const SdfPath& protoPath = protoPaths[protoIndex]; const UsdPrim& protoPrim = stage->GetPrimAtPath(protoPath); if (protoPrim) { // Get the prototype's local transformation. bool resetsXformStack; protoXform = xformCache.GetLocalTransformation(protoPrim, &resetsXformStack); } } (*xforms)[instanceId] = protoXform * instanceTransform.GetMatrix(); } return ApplyMaskToArray(mask, xforms); }
SdfPathVector SdfPath::GetConciseRelativePaths(const SdfPathVector& paths) { SdfPathVector primPaths; SdfPathVector anchors; SdfPathVector labels; // initialize the vectors TF_FOR_ALL(iter, paths) { if(!iter->IsAbsolutePath()) { TF_WARN("argument to GetConciseRelativePaths contains a relative path."); return paths; } // first, get the prim paths SdfPath primPath = iter->GetPrimPath(); SdfPath anchor = primPath.GetParentPath(); primPaths.push_back(primPath); anchors.push_back(anchor); // we have to special case root anchors, since MakeRelativePath can't handle them if(anchor == SdfPath::AbsoluteRootPath()) labels.push_back(primPath); else labels.push_back(primPath.MakeRelativePath(anchor)); } // each ambiguous path must be raised to its parent bool ambiguous; do { ambiguous = false; // the next iteration of labels SdfPathVector newAnchors; SdfPathVector newLabels; // find ambiguous labels for(size_t i=0;i<labels.size();++i) { int ok = true; // search for some other path that makes this one ambiguous for(size_t j=0;j<labels.size();++j) { if(i != j && labels[i] == labels[j] && primPaths[i] != primPaths[j]) { ok = false; break; } } if(!ok) { // walk the anchor up one node SdfPath newAnchor = anchors[i].GetParentPath(); newAnchors.push_back(newAnchor); newLabels.push_back( newAnchor == SdfPath::AbsoluteRootPath() ? primPaths[i] : primPaths[i].MakeRelativePath( newAnchor ) ); ambiguous = true; } else { newAnchors.push_back(anchors[i]); newLabels.push_back(labels[i]); } } anchors = newAnchors; labels = newLabels; } while(ambiguous); // generate the final set from the anchors SdfPathVector result; for(size_t i=0; i<anchors.size();++i) { if(anchors[i] == SdfPath::AbsoluteRootPath()) { result.push_back( paths[i] ); } else { result.push_back( paths[i].MakeRelativePath( anchors[i] )); } } return result; }