This change adds an optional UsdUtilsProcessingFunc parameter to UsdUtilsComputeAllDe...
authormatthewcpp <matthewcpp@users.noreply.github.com>
Mon, 22 Jan 2024 22:36:30 +0000 (14:36 -0800)
committerpixar-oss <pixar-oss@users.noreply.github.com>
Mon, 22 Jan 2024 23:03:27 +0000 (15:03 -0800)
(Internal change: 2312250)

pxr/usd/usdUtils/assetLocalization.cpp
pxr/usd/usdUtils/assetLocalizationDelegate.cpp
pxr/usd/usdUtils/assetLocalizationDelegate.h
pxr/usd/usdUtils/dependencies.cpp
pxr/usd/usdUtils/dependencies.h
pxr/usd/usdUtils/localizeAsset.h
pxr/usd/usdUtils/overview.dox
pxr/usd/usdUtils/testenv/testUsdUtilsDependencies.py
pxr/usd/usdUtils/userProcessingFunc.h
pxr/usd/usdUtils/wrapDependencies.cpp

index f09114f6cc1f0738e44d48c8119d68b1ed07e1e2..82811d287f1ef713139637633798374a740543eb 100644 (file)
@@ -597,7 +597,7 @@ UsdUtils_LocalizationContext::_ValueTypeIsRelevant(
 }
 
 struct UsdUtils_ExtractExternalReferencesClient {
-    void 
+    UsdUtilsDependencyInfo 
     Process (
         const SdfLayerRefPtr &, 
         const std::string &assetPath,
@@ -613,6 +613,8 @@ struct UsdUtils_ExtractExternalReferencesClient {
                 PlaceAsset(dependency, dependencyType);
             }
         }
+
+        return {};
     }
 
     void PlaceAsset(
index 0c3c4a32bbe626ffeeac6717c5f3ddf5ff1972f5..163b41e8bfe83c081440157cf92516b03238b418 100644 (file)
 
 PXR_NAMESPACE_OPEN_SCOPE
 
+static
+std::vector<std::string> 
+_AllDependenciesForInfo(
+    const UsdUtilsDependencyInfo &depInfo)
+{
+    const std::vector<std::string>& assetDeps = depInfo.GetDependencies();
+    std::vector<std::string> dependencies;
+    dependencies.reserve((assetDeps.size() + 1));
+    dependencies.insert(dependencies.end(), assetDeps.begin(), assetDeps.end());
+    dependencies.emplace_back(depInfo.GetAssetPath());
+
+    return dependencies;
+}
+
 // Processes sublayer paths, removing duplicates and only updates the paths in
 // the writable layer if the processed list differs from the source list.
 std::vector<std::string> 
@@ -377,19 +391,6 @@ UsdUtils_WritableLocalizationDelegate::_GetRelativeKeyPath(
     }
 }
 
-std::vector<std::string> 
-UsdUtils_WritableLocalizationDelegate::_AllDependenciesForInfo(
-    const UsdUtilsDependencyInfo &depInfo)
-{
-    const std::vector<std::string>& assetDeps = depInfo.GetDependencies();
-    std::vector<std::string> dependencies;
-    dependencies.reserve((assetDeps.size() + 1));
-    dependencies.insert(dependencies.end(), assetDeps.begin(), assetDeps.end());
-    dependencies.emplace_back(depInfo.GetAssetPath());
-
-    return dependencies;
-}
-
 SdfLayerRefPtr 
 UsdUtils_WritableLocalizationDelegate::_GetOrCreateWritableLayer(
     const SdfLayerRefPtr& layer)
@@ -444,8 +445,19 @@ std::vector<std::string>
 UsdUtils_ReadOnlyLocalizationDelegate::ProcessSublayers(
     const SdfLayerRefPtr &layer)
 {
+    std::vector<std::string> dependencies;
+
     for (const auto &path : layer->GetSubLayerPaths()) {
-        _processingFunc(layer, path, {}, UsdUtils_DependencyType::Sublayer);
+        UsdUtilsDependencyInfo info = _processingFunc(
+            layer, path, {}, UsdUtils_DependencyType::Sublayer);
+
+        if (info.GetAssetPath().empty()) {
+            continue;
+        }
+
+        dependencies.emplace_back(info.GetAssetPath());
+        dependencies.insert(dependencies.end(), 
+            info.GetDependencies().begin(), info.GetDependencies().end());
     }
 
     return {};
@@ -456,19 +468,9 @@ UsdUtils_ReadOnlyLocalizationDelegate::ProcessPayloads(
     const SdfLayerRefPtr &layer,
     const SdfPrimSpecHandle &primSpec)
 {
-    for (auto const& payload: primSpec->GetPayloadList().GetAppliedItems()) {
-        // If the asset path is empty this is a local payload. We can ignore
-        // these since they refer to the same layer where the payload was
-        // authored.
-        if (payload.GetAssetPath().empty()) {
-            continue;
-        }
-
-        _processingFunc(layer, payload.GetAssetPath(), {}, 
-            UsdUtils_DependencyType::Payload);
-    }
-
-    return {};
+    return ProcessReferencesOrPayloads
+        <SdfPayload, UsdUtils_DependencyType::Payload>(
+            layer, primSpec->GetPayloadList().GetAppliedItems());
 }
 
 std::vector<std::string> 
@@ -476,30 +478,41 @@ UsdUtils_ReadOnlyLocalizationDelegate::ProcessReferences(
         const SdfLayerRefPtr &layer,
         const SdfPrimSpecHandle &primSpec)
 {
-    SdfReferencesProxy references = primSpec->GetReferenceList();
-
-    for (const auto& reference: references.GetAppliedItems()) {
-        _ProcessRefOrPayload<SdfReference, UsdUtils_DependencyType::Reference>(
-            layer, reference);
-    }
-
-    return {};
+    return ProcessReferencesOrPayloads
+        <SdfReference, UsdUtils_DependencyType::Reference>(
+            layer, primSpec->GetReferenceList().GetAppliedItems());
 }
 
-template <class RefOrPayloadType, UsdUtils_DependencyType DEP_TYPE>
-void 
-UsdUtils_ReadOnlyLocalizationDelegate::_ProcessRefOrPayload(
+
+template <typename RefOrPayloadType, UsdUtils_DependencyType dependencyType>
+std::vector<std::string> 
+UsdUtils_ReadOnlyLocalizationDelegate::ProcessReferencesOrPayloads(
     const SdfLayerRefPtr &layer,
-    const RefOrPayloadType& refOrPayload)
+    const std::vector<RefOrPayloadType>& appliedItems)
 {
-    // If the asset path is empty this is a local ref/payload. We can ignore
-    // these since they refer to the same layer where the payload was
-    // authored.
-    if (refOrPayload.GetAssetPath().empty()) {
-        return;
+    std::vector<std::string> dependencies;
+
+    for (const auto& refOrPayload: appliedItems) {
+        // If the asset path is empty this is a local reference or payload.
+        // We can ignore these since they refer to the same layer where it was 
+        // authored.
+        if (refOrPayload.GetAssetPath().empty()) {
+            continue;
+        }
+
+        UsdUtilsDependencyInfo info = _processingFunc(layer, 
+            refOrPayload.GetAssetPath(), {}, dependencyType);
+
+        if (info.GetAssetPath().empty()) {
+            continue;
+        }
+
+        dependencies.emplace_back(info.GetAssetPath());
+        dependencies.insert(dependencies.end(), 
+            info.GetDependencies().begin(), info.GetDependencies().end());
     }
 
-    _processingFunc(layer, refOrPayload.GetAssetPath(), {}, DEP_TYPE);
+    return dependencies;
 }
 
 std::vector<std::string>
@@ -509,8 +522,8 @@ UsdUtils_ReadOnlyLocalizationDelegate::ProcessValuePath(
     const std::string &authoredPath,
     const std::vector<std::string> &dependencies)
 {
-    _processingFunc(layer, authoredPath, dependencies
-        UsdUtils_DependencyType::Reference);
+    return _AllDependenciesForInfo(_processingFunc(layer, authoredPath
+        dependencies, UsdUtils_DependencyType::Reference));
 
     return {};
 }
@@ -522,10 +535,8 @@ UsdUtils_ReadOnlyLocalizationDelegate::ProcessValuePathArrayElement(
     const std::string &authoredPath,
     const std::vector<std::string> &dependencies)
 {    
-    _processingFunc(layer, authoredPath, dependencies, 
-        UsdUtils_DependencyType::Reference);
-
-    return {};
+    return _AllDependenciesForInfo(_processingFunc(layer, authoredPath, 
+        dependencies, UsdUtils_DependencyType::Reference));
 }
 
 std::vector<std::string>
@@ -536,10 +547,9 @@ UsdUtils_ReadOnlyLocalizationDelegate::ProcessClipTemplateAssetPath(
     const std::string &templateAssetPath,
     std::vector<std::string> dependencies)
 {
-    _processingFunc(layer, templateAssetPath, dependencies, 
-        UsdUtils_DependencyType::ClipTemplateAssetPath);
-
-    return {};
+    return _AllDependenciesForInfo(_processingFunc(
+        layer, templateAssetPath, dependencies, 
+        UsdUtils_DependencyType::ClipTemplateAssetPath));
 }
 
 PXR_NAMESPACE_CLOSE_SCOPE
index 13c9124d12b15e89bfbea96d7c1f876b8492ddf7..fc6cdb78ccb4bf4b4897db21dad5ce23cff42ff9 100644 (file)
@@ -217,9 +217,6 @@ private:
     SdfLayerRefPtr _GetOrCreateWritableLayer(const SdfLayerRefPtr& layer);
 
     static std::string _GetRelativeKeyPath(const std::string& fullPath);
-        
-    static std::vector<std::string> _AllDependenciesForInfo(
-        const UsdUtilsDependencyInfo &depInfo);
 
     // the user supplied processing function that will be invoked on every path.
     ProcessingFunc _processingFunc;
@@ -247,7 +244,7 @@ class UsdUtils_ReadOnlyLocalizationDelegate
 : public UsdUtils_LocalizationDelegate
 {
 public:
-    using ProcessingFunc = std::function<void(
+    using ProcessingFunc = std::function<UsdUtilsDependencyInfo(
             const SdfLayerRefPtr &layer, 
             const std::string &assetPath,
             const std::vector<std::string> &dependencies,
@@ -287,10 +284,10 @@ public:
         std::vector<std::string> dependencies) override;
 
 private:
-    template <class RefOrPayloadType, UsdUtils_DependencyType DEP_TYPE>
-    void _ProcessRefOrPayload(
+    template <typename RefOrPayloadType, UsdUtils_DependencyType DepType>
+    std::vector<std::string> ProcessReferencesOrPayloads(
         const SdfLayerRefPtr &layer,
-        const RefOrPayloadType& refOrPayload);
+        const std::vector<RefOrPayloadType>& appliedItems);
 
     ProcessingFunc _processingFunc;
 };
index 646d7b87f2e3aeb07ac58a7e9d607dc356d10d99..23ae7609355501a0d716358b4b3093ea0d764c24 100644 (file)
@@ -59,13 +59,43 @@ UsdUtilsExtractExternalReferences(
 
 struct UsdUtils_ComputeAllDependenciesClient
 {
-    void 
+    UsdUtils_ComputeAllDependenciesClient(
+        const std::function<UsdUtilsProcessingFunc> &processingFunc)
+            :processingFunc(processingFunc) {}
+
+    UsdUtilsDependencyInfo 
     Process( 
         const SdfLayerRefPtr &layer, 
         const std::string & assetPath,
         const std::vector<std::string> &dependencies,
         UsdUtils_DependencyType dependencyType)
     {
+        
+        if (processingFunc) {
+            UsdUtilsDependencyInfo depInfo = {assetPath, dependencies};
+            UsdUtilsDependencyInfo processedInfo = 
+                processingFunc(layer, depInfo);
+            
+            if (processedInfo.GetAssetPath().empty()) {
+                return {};
+            }
+
+            // When using a processing function with template asset paths
+            // such as clips or udim, if the user does not modify the
+            // asset path, we do not want to place it in the resulting arrays
+            // We always want to add dependencies, however
+            bool originalPathIsTemplate = !dependencies.empty();
+            if (processedInfo != depInfo || !originalPathIsTemplate) {
+                PlaceAsset(layer, processedInfo.GetAssetPath(), dependencyType);
+            }
+            
+            for (const auto & dependency : processedInfo.GetDependencies()) {
+                PlaceAsset(layer, dependency, dependencyType);
+            }
+
+            return processedInfo;
+        }
+
         if (dependencies.empty()) {
             PlaceAsset(layer, assetPath, dependencyType);
         }
@@ -74,6 +104,8 @@ struct UsdUtils_ComputeAllDependenciesClient
                 PlaceAsset(layer, dependency, dependencyType);
             }
         }
+
+        return {};
     }
 
     bool 
@@ -120,6 +152,7 @@ struct UsdUtils_ComputeAllDependenciesClient
 
     std::vector<SdfLayerRefPtr> layers;
     std::vector<std::string> assets, unresolvedPaths;
+    std::function<UsdUtilsProcessingFunc> processingFunc;
 };
 
 bool
@@ -127,7 +160,8 @@ UsdUtilsComputeAllDependencies(
     const SdfAssetPath &assetPath,
     std::vector<SdfLayerRefPtr> *outLayers,
     std::vector<std::string> *outAssets,
-    std::vector<std::string> *outUnresolvedPaths)
+    std::vector<std::string> *outUnresolvedPaths,
+    const std::function<UsdUtilsProcessingFunc> &processingFunc)
 {
     SdfLayerRefPtr rootLayer = SdfLayer::FindOrOpen(assetPath.GetAssetPath());
 
@@ -135,7 +169,7 @@ UsdUtilsComputeAllDependencies(
         return false;
     }
 
-    UsdUtils_ComputeAllDependenciesClient client;
+    UsdUtils_ComputeAllDependenciesClient client(processingFunc);
     UsdUtils_ReadOnlyLocalizationDelegate delegate(
         std::bind(&UsdUtils_ComputeAllDependenciesClient::Process, &client,
             std::placeholders::_1, std::placeholders::_2, 
index 808f63fb657f0633386afeaaf0157384b7376119..c187847705abd7665ac0071140ed793c87e42831 100644 (file)
@@ -38,6 +38,7 @@
 #include "pxr/pxr.h"
 #include "pxr/usd/usdUtils/api.h"
 #include "pxr/usd/usdUtils/usdzPackage.h"
+#include "pxr/usd/usdUtils/userProcessingFunc.h"
 
 #include <string>
 #include <vector>
@@ -68,24 +69,33 @@ void UsdUtilsExtractExternalReferences(
 /// All of the resolved non-layer dependencies are populated in \p assets.
 /// Any unresolved (layer and non-layer) asset paths are populated in 
 /// \p unresolvedPaths.
-/// 
+///
+/// If a function is provided for the \p processingFunc parameter, it will be
+/// invoked on every asset path that is discovered during localization.  
+/// Refer to \ref UsdUtilsDependencyInfo for general information on User 
+/// processing functions.  Any changes made to the paths during the 
+/// invocation of this function will not be written to processed layers.
+///
 /// The input vectors to be populated with the results are *cleared* before 
 /// any results are added to them.
 /// 
 /// Returns true if the given asset was resolved correctly.
 USDUTILS_API
 bool
-UsdUtilsComputeAllDependencies(const SdfAssetPath &assetPath,
-                               std::vector<SdfLayerRefPtr> *layers,
-                               std::vector<std::string> *assets,
-                               std::vector<std::string> *unresolvedPaths);
+UsdUtilsComputeAllDependencies(
+    const SdfAssetPath &assetPath,
+    std::vector<SdfLayerRefPtr> *layers,
+    std::vector<std::string> *assets,
+    std::vector<std::string> *unresolvedPaths,
+    const std::function<UsdUtilsProcessingFunc> &processingFunc = 
+        std::function<UsdUtilsProcessingFunc>());
 
 /// Callback that is used to modify asset paths in a layer.  The \c assetPath
 /// will contain the string value that's authored.  The returned value is the
 /// new value that should be authored in the layer.  If the function returns
 /// an empty string, that value will be removed from the layer.
 using UsdUtilsModifyAssetPathFn = std::function<std::string(
-        const std::string& assetPath)>;
+    const std::string& assetPath)>;
 
 /// Helper function that visits every asset path in \c layer, calls \c modifyFn
 /// and replaces the value with the return value of \c modifyFn.  This modifies
@@ -97,8 +107,8 @@ using UsdUtilsModifyAssetPathFn = std::function<std::string(
 /// for example.
 USDUTILS_API
 void UsdUtilsModifyAssetPaths(
-        const SdfLayerHandle& layer,
-        const UsdUtilsModifyAssetPathFn& modifyFn);
+    const SdfLayerHandle& layer,
+    const UsdUtilsModifyAssetPathFn& modifyFn);
 
 PXR_NAMESPACE_CLOSE_SCOPE
 
index 39945af0ee4362035693b3ecbe638d7e655bcd74..7035efa40b99b44d4813f03699c507f592dbc73b 100644 (file)
@@ -65,7 +65,12 @@ class SdfAssetPath;
 /// If a function is provided for the \p processingFunc parameter, it will be
 /// invoked on every asset path that is discovered during localization. This
 /// allows you to inject your own logic into the process. Refer to
-/// \ref UsdUtilsDependencyInfo for additional information.
+/// \ref UsdUtilsDependencyInfo for general information on user processing
+/// functions.  If an asset path is ignored in the processing function, it will
+/// be removed from the layer and excluded from the localized package.  Paths
+/// that are modified will have their updated value written back into the
+/// localized layer. Paths that are added to the dependencies array during
+/// processing will be included in the resulting localized asset.
 /// 
 /// Returns true if the package was created successfully.
 /// 
index b2082a26eed85fde1b70a89dd14fe14845376458..a319a22551d8316bdcb1ea291390c4f1b90c8610 100644 (file)
@@ -57,9 +57,9 @@ traversal.
 Processing functions are able to perform a number of tasks which have an effect 
 on the resulting output:
 
-\li Modify the asset path that is written back into the layer
-\li Add additional dependencies to be included in the output
-\li Remove an asset path and/or dependencies altogether from the output
+\li Modify an asset path to contain a new value.
+\li Add additional dependencies to be included in the output.
+\li Remove an asset path and/or dependencies altogether from the output.
 
 Creating a processing function allows you to create customized output without 
 having to write dependency discovery and traversal code.
@@ -122,7 +122,7 @@ def ProcessingFunc(layer, dependencyInfo):
 \endcode
 
 After a processing function has been called, the system looks at the returned
-asset path.  If it is empty, then the reference to that that path is removed,
+asset path.  If it is empty, then the reference to that path is removed,
 otherwise the new value is placed in the layer. Additionally, each item in the 
 dependencies array is added to the resulting package and enqueued for 
 recursive traversal.
index 5877ca8e934a9ca57db6b517d6e802f741b0ce0c..31698b2841003c4e3c25bbb2f7630e0f1d667a62 100644 (file)
@@ -23,6 +23,7 @@
 # language governing permissions and limitations under the Apache License.
 
 from pxr import UsdUtils, Sdf, Usd
+from pathlib import Path
 import os
 import unittest
 
@@ -30,9 +31,9 @@ class TestUsdUtilsDependencies(unittest.TestCase):
     def test_ComputeAllDependencies(self):
         """Basic test for UsdUtils.ComputeAllDependencies"""
 
-        def _testLayer(rootLayer):
+        def _testLayer(rootLayer, processingFunc):
             layers, assets, unresolved = \
-                UsdUtils.ComputeAllDependencies(rootLayer)
+                UsdUtils.ComputeAllDependencies(rootLayer, processingFunc)
 
             self.assertEqual(
                set(layers),
@@ -78,18 +79,21 @@ class TestUsdUtilsDependencies(unittest.TestCase):
                                "v_attr_a_nonexist.txt",
                                "v_attr_nonexist.usd"]]))
 
-        def _test(rootLayer):
-            _testLayer(rootLayer)
+        def _test(rootLayer, processingFunc = None):
+            _testLayer(rootLayer, processingFunc)
 
             layer = Sdf.Layer.FindOrOpen(rootLayer)
             layer.SetPermissionToEdit(False)
-            _testLayer(rootLayer)
+            _testLayer(rootLayer, processingFunc)
 
         _test("computeAllDependencies/ascii.usda")
         _test("computeAllDependencies/ascii.usd")
         _test("computeAllDependencies/crate.usdc")
         _test("computeAllDependencies/crate.usd")
 
+        # test identity processing func
+        _test("computeAllDependencies/ascii.usda", lambda _, info: info)
+
     def test_ComputeAllDependenciesInvalidClipTemplate(self):
         """Test that an invalid clip template asset path does not
         cause an exception in UsdUtils.ComputeAllDependencies."""
@@ -140,6 +144,129 @@ class TestUsdUtilsDependencies(unittest.TestCase):
             Sdf.Layer.Find("anon_sublayer.usda")])
         self.assertEqual(unresolved, ["unresolved.usda"])
 
+    def test_ComputeAllDependenciesUserFuncFilterPaths(self):
+        """Tests paths that are filtered by the processing func 
+        do not appear in results"""
+
+        stagePath = "test_filter.usda"
+        assetDirPath = "./asset_dep_dir"
+        assetFilePath = "./non_dir_dep.usda"
+
+        if not os.path.exists(assetDirPath): os.mkdir(assetDirPath)
+        assetDepLayer = Sdf.Layer.CreateNew(assetFilePath)
+        assetDepLayer.Save()
+
+        stage = Usd.Stage.CreateNew(stagePath)
+        prim = stage.DefinePrim("/test")
+        dirAttr = prim.CreateAttribute("dirAsset", Sdf.ValueTypeNames.Asset)
+        dirAttr.Set(assetDirPath)
+        nonDirAttr = prim.CreateAttribute("depAsset", Sdf.ValueTypeNames.Asset)
+        nonDirAttr.Set(assetFilePath)
+        stage.GetRootLayer().Save()
+
+        def FilterDirectories(layer, depInfo):
+            if (os.path.isdir(depInfo.assetPath)):
+                return UsdUtils.DependencyInfo()
+            else:
+                return depInfo
+
+        layers, references, unresolved = \
+            UsdUtils.ComputeAllDependencies(stagePath, FilterDirectories)
+        
+        self.assertEqual(layers, [stage.GetRootLayer(), assetDepLayer])
+        self.assertEqual(references, [])
+        self.assertEqual(unresolved, [])
+
+    def test_ComputeAllDependenciesUserFuncAdditionalPaths(self):
+        """Tests additional paths that are specified by the user processing func
+        appear in results"""
+
+        stagePath = "test_additional_deps.usda"
+        assetPath = "additional_dep.txt"
+        assetPathDep = "additional_dep.txt2"
+        Path(assetPath).touch()
+        Path(assetPathDep).touch()
+        stage = Usd.Stage.CreateNew(stagePath)
+        prim = stage.DefinePrim("/test")
+        attr = prim.CreateAttribute("depAsset", Sdf.ValueTypeNames.Asset)
+        attr.Set(assetPath)
+        stage.GetRootLayer().Save()
+
+        def AddAdditionalDeps(layer, depInfo):
+            return UsdUtils.DependencyInfo(
+                depInfo.assetPath, [depInfo.assetPath + "2"])
+
+
+        layers, references, unresolved = \
+            UsdUtils.ComputeAllDependencies(stagePath, AddAdditionalDeps)
+        
+        self.assertEqual(layers, [stage.GetRootLayer()])
+        self.assertEqual([os.path.normcase(f) for f in references], 
+                         [os.path.normcase(os.path.abspath(assetPath)), 
+                          os.path.normcase(os.path.abspath(assetPathDep))])
+        self.assertEqual(unresolved, [])
+
+    def test_ComputeAllDependenciesUserFuncModifyPathss(self):
+        """Tests assets paths which are modified by the processing func
+        appear correctly in returned vectors"""
+
+        stagePath = "test_modified_deps.usda"
+        assetPath = "modified_dep.txt"
+        Path(assetPath).touch()
+        stage = Usd.Stage.CreateNew(stagePath)
+        prim = stage.DefinePrim("/test")
+        attr = prim.CreateAttribute("depAsset", Sdf.ValueTypeNames.Asset)
+        attr.Set("dep.txt")
+        stage.GetRootLayer().Save()
+
+        def ModifyDeps(layer, depInfo):
+            return UsdUtils.DependencyInfo("modified_" + depInfo.assetPath)
+
+
+        layers, references, unresolved = \
+            UsdUtils.ComputeAllDependencies(stagePath, ModifyDeps)
+        
+        self.assertEqual(layers, [stage.GetRootLayer()])
+        self.assertEqual([os.path.normcase(f) for f in references],
+            [os.path.normcase(os.path.abspath(assetPath))])
+        self.assertEqual(unresolved, [])
+
+    def test_ComputeAllDependenciesParseAdditionalLayers(self):
+        """Tests that layers that are specified as additional dependencies are
+        themselves processed for additional assets"""
+
+        def CreateStageWithDep(stagePath, depPath):
+            stage = Usd.Stage.CreateNew(stagePath)
+            prim = stage.DefinePrim("/test")
+            if depPath is not None:
+                attr = prim.CreateAttribute("depAsset", Sdf.ValueTypeNames.Asset)
+                attr.Set(depPath)
+            stage.GetRootLayer().Save()
+            return stage
+        
+        stagePath = "test_process_deps.usda"
+        asset = CreateStageWithDep(stagePath, "dep.usda")
+        dep = CreateStageWithDep("dep.usda", None)
+        extra = CreateStageWithDep("extra_dep.usda", "file.txt")
+        Path("file.txt").touch()
+
+        def AddExtra(layer, depInfo):
+            if depInfo.assetPath.startswith("dep"):
+                return UsdUtils.DependencyInfo(
+                    depInfo.assetPath, ["extra_" + depInfo.assetPath])
+            else:
+                return depInfo
+            
+        layers, references, unresolved = \
+            UsdUtils.ComputeAllDependencies(stagePath, AddExtra)
+        
+        self.assertEqual(layers, [asset.GetRootLayer(), dep.GetRootLayer(), 
+                                  extra.GetRootLayer()])
+        self.assertEqual([os.path.normcase(f) for f in references],
+            [os.path.normcase(os.path.abspath("file.txt"))])
+        self.assertEqual(unresolved, [])
+            
+
 
 if __name__=="__main__":
     unittest.main()
index 3c3c1d0ecdcbc1460a123695763b2e33ef9806e0..15126603fca11d69bb3e67522e8b5c09395477f3 100644 (file)
@@ -54,14 +54,20 @@ public:
         : _assetPath(assetPath), _dependencies(dependencies) {}
 
     /// Returns the asset value path for the dependency.
-    /// When returned as a parameter from a user processing function, This value
-    /// is checked by the localization system to see if it differs from the
-    /// original authored value. If the returned value is set to and empty 
-    /// string, the asset will be removed from the layer and not included in the
-    /// resulting package.  If this value differs from what what was originally
-    /// authored into the layer, the path will be updated with this new value.
-    /// Additionally, the newly specified asset will be included in the package
-    /// and searched for additional dependencies if it can be opened as a layer. 
+    /// When UsdUtilsDependencyInfo is returned as a parameter from a user
+    /// processing function, the localization system compares the value
+    /// with the value that was originally authored in the layer.
+    ///
+    /// If the values are the same, no special action is taken and processing
+    /// will continue as normal.
+    ///
+    /// If the returned value is an empty string, the system will ignore this
+    /// path as well as any dependencies associated with it.
+    ///
+    /// If the returned value differs from what what was originally
+    /// authored into the layer, the system will instead operate on the updated.
+    /// value.  If the updated path can be opened as a layer, it will be 
+    /// enqueued and searched for additional dependencies.
     USDUTILS_API const std::string& GetAssetPath() const {
         return _assetPath;
     }
@@ -71,22 +77,35 @@ public:
     /// When passed into the user processing function, if this array is
     /// populated, then the asset path resolved to one or more values, such as
     /// in the case of UDIM tiles or clip asset path template strings.
-    /// When this structure is returned from a processing function, the paths
-    /// contained within will be included in packaged output in addition to the
-    /// value specified in the dependency's asset path. Any paths that can be
-    /// opened as layers will be recursively searched for further dependencies.
+    ///
+    /// When this structure is returned from a processing function, each path
+    /// contained within will in turn be processed by the system. Any path 
+    /// that can be opened as a layer, will be enqueued and searched for 
+    /// additional dependencies.
     USDUTILS_API const std::vector<std::string>& GetDependencies() const {
         return _dependencies;
     }
 
+    /// Equality: Asset path and dependencies are the same
+    bool operator==(const UsdUtilsDependencyInfo &rhs) const {
+        return _assetPath == rhs._assetPath &&
+               _dependencies == rhs._dependencies;
+    }
+
+    /// Inequality operator
+    /// \sa UsdUtilsDependencyInfo::operator==(const UsdUtilsDependencyInfo&)
+    bool operator!=(const UsdUtilsDependencyInfo& rhs) const {
+        return !(*this == rhs);
+    }
+
 private:
     std::string _assetPath;
     std::vector<std::string> _dependencies;
 };
 
 /// Signature for user supplied processing function.
-/// \param layer The layer containing this dependency
-/// \param dependencyInfo contains asset path information for this dependency
+/// \param layer The layer containing this dependency.
+/// \param dependencyInfo contains asset path information for this dependency.
 using UsdUtilsProcessingFunc = UsdUtilsDependencyInfo(
     const SdfLayerHandle &layer, 
     const UsdUtilsDependencyInfo &dependencyInfo);
index 3a809172272d02e76fa20e92bb9ee354e27fc9e8..1b62068d1c56f1d05a91c65de3ee1a9a993e4d1d 100644 (file)
@@ -62,13 +62,15 @@ _LayerRefToObj(const SdfLayerRefPtr& layer)
 }
 
 static bp::tuple
-_ComputeAllDependencies(const SdfAssetPath &assetPath) 
+_ComputeAllDependencies(
+    const SdfAssetPath &assetPath,
+    std::function<UsdUtilsProcessingFunc> processingFunc) 
 {
     std::vector<SdfLayerRefPtr> layers;
     std::vector<std::string> assets, unresolvedPaths;
     
     UsdUtilsComputeAllDependencies(assetPath, &layers, &assets, 
-                                   &unresolvedPaths);
+                                   &unresolvedPaths, processingFunc);
     bp::list layersList;
     for (auto &l: layers) { 
         layersList.append(_LayerRefToObj(l)); 
@@ -96,7 +98,8 @@ void wrapDependencies()
              bp::arg("editLayersInPlace") = false));
 
     bp::def("ComputeAllDependencies", _ComputeAllDependencies,
-            (bp::arg("assetPath")));
+            (bp::arg("assetPath"),
+             bp::arg("processingFunc") = bp::object()));
 
     using Py_UsdUtilsModifyAssetPathFn = std::string(const std::string&);
     TfPyFunctionFromPython<Py_UsdUtilsModifyAssetPathFn>();