diff --git a/lib/mayaUsd/fileio/chaser/exportChaser.cpp b/lib/mayaUsd/fileio/chaser/exportChaser.cpp index 43aed4d013..4edfc38c16 100644 --- a/lib/mayaUsd/fileio/chaser/exportChaser.cpp +++ b/lib/mayaUsd/fileio/chaser/exportChaser.cpp @@ -35,4 +35,14 @@ bool UsdMayaExportChaser::PostExport() return true; } +void UsdMayaExportChaser::RegisterExtraPrimsPaths(const std::vector& extraPrimPaths) +{ + _extraPrimsPaths.insert(_extraPrimsPaths.end(), extraPrimPaths.begin(), extraPrimPaths.end()); +} + +const std::vector& UsdMayaExportChaser::GetExtraPrimsPaths() const +{ + return _extraPrimsPaths; +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/fileio/chaser/exportChaser.h b/lib/mayaUsd/fileio/chaser/exportChaser.h index 9a839ad96d..502212408a 100644 --- a/lib/mayaUsd/fileio/chaser/exportChaser.h +++ b/lib/mayaUsd/fileio/chaser/exportChaser.h @@ -21,6 +21,7 @@ #include #include #include +#include #include PXR_NAMESPACE_OPEN_SCOPE @@ -69,6 +70,20 @@ class UsdMayaExportChaser : public TfRefBase /// Returning false will terminate the whole export. MAYAUSD_CORE_PUBLIC virtual bool PostExport(); + + /// Optional helper method to cache the given path array in the chaser. + /// The cached array is used internally to track any extra prim created by the chasers. + /// For example: when duplicating Maya data to USD we'll internally ask the chaser for those. + MAYAUSD_CORE_PUBLIC + virtual void RegisterExtraPrimsPaths(const std::vector& extraPrimPaths); + + /// Get the array of extra prim paths set by the chaser. + /// Returns the array of cached prim paths. + MAYAUSD_CORE_PUBLIC + virtual const std::vector& GetExtraPrimsPaths() const; + +private: + std::vector _extraPrimsPaths; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/fileio/jobs/jobArgs.cpp b/lib/mayaUsd/fileio/jobs/jobArgs.cpp index 2ae18b9eca..04669cfb99 100644 --- a/lib/mayaUsd/fileio/jobs/jobArgs.cpp +++ b/lib/mayaUsd/fileio/jobs/jobArgs.cpp @@ -729,6 +729,7 @@ UsdMayaJobExportArgs::UsdMayaJobExportArgs( , ignoreWarnings(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->ignoreWarnings)) , includeEmptyTransforms( extractBoolean(userArgs, UsdMayaJobExportArgsTokens->includeEmptyTransforms)) + , isDuplicating(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->isDuplicating)) , materialCollectionsPath( extractAbsolutePath(userArgs, UsdMayaJobExportArgsTokens->materialCollectionsPath)) , materialsScopeName(_GetMaterialsScopeName( @@ -847,7 +848,7 @@ std::ostream& operator<<(std::ostream& out, const UsdMayaJobExportArgs& exportAr << "file: " << exportArgs.file << std::endl << "ignoreWarnings: " << TfStringify(exportArgs.ignoreWarnings) << std::endl << "includeEmptyTransforms: " << TfStringify(exportArgs.includeEmptyTransforms) - << std::endl; + << "isDuplicating: " << TfStringify(exportArgs.isDuplicating) << std::endl; out << "includeAPINames (" << exportArgs.includeAPINames.size() << ")" << std::endl; for (const std::string& includeAPIName : exportArgs.includeAPINames) { out << " " << includeAPIName << std::endl; @@ -1131,6 +1132,7 @@ const VtDictionary& UsdMayaJobExportArgs::GetDefaultDictionary() d[UsdMayaJobExportArgsTokens->filterTypes] = std::vector(); d[UsdMayaJobExportArgsTokens->ignoreWarnings] = false; d[UsdMayaJobExportArgsTokens->includeEmptyTransforms] = true; + d[UsdMayaJobExportArgsTokens->isDuplicating] = false; d[UsdMayaJobExportArgsTokens->kind] = std::string(); d[UsdMayaJobExportArgsTokens->disableModelKindProcessor] = false; d[UsdMayaJobExportArgsTokens->materialCollectionsPath] = std::string(); @@ -1236,6 +1238,7 @@ const VtDictionary& UsdMayaJobExportArgs::GetGuideDictionary() d[UsdMayaJobExportArgsTokens->filterTypes] = _stringVector; d[UsdMayaJobExportArgsTokens->ignoreWarnings] = _boolean; d[UsdMayaJobExportArgsTokens->includeEmptyTransforms] = _boolean; + d[UsdMayaJobExportArgsTokens->isDuplicating] = _boolean; d[UsdMayaJobExportArgsTokens->kind] = _string; d[UsdMayaJobExportArgsTokens->disableModelKindProcessor] = _boolean; d[UsdMayaJobExportArgsTokens->materialCollectionsPath] = _string; diff --git a/lib/mayaUsd/fileio/jobs/jobArgs.h b/lib/mayaUsd/fileio/jobs/jobArgs.h index 224c3c6b5b..77f74b3310 100644 --- a/lib/mayaUsd/fileio/jobs/jobArgs.h +++ b/lib/mayaUsd/fileio/jobs/jobArgs.h @@ -93,6 +93,7 @@ TF_DECLARE_PUBLIC_TOKENS( (filterTypes) \ (ignoreWarnings) \ (includeEmptyTransforms) \ + (isDuplicating) \ (kind) \ (disableModelKindProcessor) \ (materialCollectionsPath) \ @@ -243,6 +244,7 @@ struct UsdMayaJobExportArgs const std::string file; const bool ignoreWarnings; const bool includeEmptyTransforms; + const bool isDuplicating; /// If this is not empty, then a set of collections are exported on the /// prim pointed to by the path, each representing the collection of diff --git a/lib/mayaUsd/fileio/jobs/writeJob.cpp b/lib/mayaUsd/fileio/jobs/writeJob.cpp index 66e87c305b..ef7e887268 100644 --- a/lib/mayaUsd/fileio/jobs/writeJob.cpp +++ b/lib/mayaUsd/fileio/jobs/writeJob.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -35,13 +34,11 @@ #include #include #include -#include #include #include #include #include -#include // Needed for directly removing a UsdVariant via Sdf // Remove when UsdVariantSet::RemoveVariant() is exposed // XXX [bug 75864] @@ -50,7 +47,6 @@ #include #include #include -#include #include #include #include @@ -59,11 +55,9 @@ #include #include -#include #include #include #include -#include #include #include #include @@ -702,12 +696,21 @@ bool UsdMaya_WriteJob::_FinishWriting() primWriterLoop.loopAdvance(); } + _extrasPrimsPaths.clear(); + // Run post export function on the chasers. MayaUsd::ProgressBarLoopScope chasersLoop(mChasers.size()); for (const UsdMayaExportChaserRefPtr& chaser : mChasers) { if (!chaser->PostExport()) { return false; } + + // Collect extra prims paths from chasers + _extrasPrimsPaths.insert( + _extrasPrimsPaths.end(), + chaser->GetExtraPrimsPaths().begin(), + chaser->GetExtraPrimsPaths().end()); + chasersLoop.loopAdvance(); } diff --git a/lib/mayaUsd/fileio/jobs/writeJob.h b/lib/mayaUsd/fileio/jobs/writeJob.h index dacdebae7a..a6a5b96d22 100644 --- a/lib/mayaUsd/fileio/jobs/writeJob.h +++ b/lib/mayaUsd/fileio/jobs/writeJob.h @@ -60,6 +60,10 @@ class UsdMaya_WriteJob MAYAUSD_CORE_PUBLIC const std::vector& GetMaterialPaths() { return mJobCtx.GetMaterialPaths(); } + // Cached prims paths from chasers + MAYAUSD_CORE_PUBLIC + const std::vector& GetExtraPrimsPaths() { return _extrasPrimsPaths; } + private: /// Begins constructing the USD stage, writing out the values at the default /// time. Returns \c true if the stage can be created successfully. @@ -103,6 +107,9 @@ class UsdMaya_WriteJob UsdMayaUtil::MDagPathMap mDagPathToUsdPathMap; + // Array to track any extra prims created chasers + std::vector _extrasPrimsPaths; + // Currently only used if stripNamespaces is on, to ensure we don't have clashes TfHashMap mUsdPathToDagPathMap; diff --git a/lib/mayaUsd/fileio/primUpdaterManager.cpp b/lib/mayaUsd/fileio/primUpdaterManager.cpp index 77d1ab2f64..38bc3cc825 100644 --- a/lib/mayaUsd/fileio/primUpdaterManager.cpp +++ b/lib/mayaUsd/fileio/primUpdaterManager.cpp @@ -16,7 +16,6 @@ #include "primUpdaterManager.h" #include -#include #include #include #include @@ -38,7 +37,6 @@ #include #include -#include #include #include @@ -520,6 +518,7 @@ struct PushExportResult SdfLayerRefPtr layer; std::shared_ptr usdToDag; std::vector materialPaths; + std::vector extraPrimsPaths; }; PushExportResult pushExport(const MObject& mayaObject, const UsdMayaPrimUpdaterContext& context) @@ -587,6 +586,7 @@ PushExportResult pushExport(const MObject& mayaObject, const UsdMayaPrimUpdaterC if (!writeJob.Write(fileName, false /* append */)) { return result; } + result.extraPrimsPaths = writeJob.GetExtraPrimsPaths(); progressBar.advance(); result.srcRootPath = writeJob.MapDagPathToSdfPath(dagPath); @@ -1663,6 +1663,7 @@ std::vector PrimUpdaterManager::duplicateToUsd( // Setting the export-selected flag will allow filtering materials so that // only materials in the prim selected to be copied will be included. ctxArgs[UsdMayaJobExportArgsTokens->exportSelected] = true; + ctxArgs[UsdMayaJobExportArgsTokens->isDuplicating] = true; const UsdStageRefPtr dstStage = dstProxyShape->getUsdStage(); const SdfLayerHandle& layer = dstStage->GetEditTarget().GetLayer(); @@ -1714,15 +1715,15 @@ std::vector PrimUpdaterManager::duplicateToUsd( options.progressBar = &progressBar; options.mergeScopes = true; + std::vector primsToCopy = { pushExportResult.srcRootPath }; + primsToCopy.reserve(primsToCopy.size() + pushExportResult.extraPrimsPaths.size()); + primsToCopy.insert( + primsToCopy.end(), + pushExportResult.extraPrimsPaths.begin(), + pushExportResult.extraPrimsPaths.end()); + CopyLayerPrimsResult copyResult = copyLayerPrims( - srcStage, - srcLayer, - srcParentPath, - dstStage, - dstLayer, - dstParentPath, - { pushExportResult.srcRootPath }, - options); + srcStage, srcLayer, srcParentPath, dstStage, dstLayer, dstParentPath, primsToCopy, options); context._pushExtras.finalize(MayaUsd::ufe::stagePath(dstStage), copyResult.renamedPaths); diff --git a/lib/mayaUsd/python/wrapExportChaser.cpp b/lib/mayaUsd/python/wrapExportChaser.cpp index 05dfc4b9ec..18430c53cb 100644 --- a/lib/mayaUsd/python/wrapExportChaser.cpp +++ b/lib/mayaUsd/python/wrapExportChaser.cpp @@ -176,6 +176,16 @@ void wrapExportChaser() .def("ExportDefault", &This::ExportDefault, &ExportChaserWrapper::default_ExportDefault) .def("ExportFrame", &This::ExportFrame, &ExportChaserWrapper::default_ExportFrame) .def("PostExport", &This::PostExport, &ExportChaserWrapper::default_PostExport) + .def( + "RegisterExtraPrimsPaths", + &This::RegisterExtraPrimsPaths, + boost::python::arg("extraPrimPaths"), + "Method to cache the path for any extra prim path created by the chaser.") + .def( + "GetExtraPrimsPaths", + &This::GetExtraPrimsPaths, + boost::python::return_internal_reference<>(), + "Get the array of the currently cached extra paths.") .def("Register", &ExportChaserWrapper::Register) .staticmethod("Register") .def("Unregister", &ExportChaserWrapper::Unregister) diff --git a/lib/mayaUsd/python/wrapPrimWriter.cpp b/lib/mayaUsd/python/wrapPrimWriter.cpp index 1fb2edfc6f..4169cb680d 100644 --- a/lib/mayaUsd/python/wrapPrimWriter.cpp +++ b/lib/mayaUsd/python/wrapPrimWriter.cpp @@ -575,6 +575,7 @@ void wrapJobExportArgs() &UsdMayaJobExportArgs::geomSidedness, return_value_policy())) .def_readonly("ignoreWarnings", &UsdMayaJobExportArgs::ignoreWarnings) .def_readonly("includeEmptyTransforms", &UsdMayaJobExportArgs::includeEmptyTransforms) + .def_readonly("isDuplicating", &UsdMayaJobExportArgs::isDuplicating) .add_property( "includeAPINames", make_getter( diff --git a/test/lib/mayaUsd/fileio/testExportChaserJobContext.py b/test/lib/mayaUsd/fileio/testExportChaserJobContext.py index 27649f8bda..233779a3e3 100644 --- a/test/lib/mayaUsd/fileio/testExportChaserJobContext.py +++ b/test/lib/mayaUsd/fileio/testExportChaserJobContext.py @@ -23,6 +23,9 @@ from maya import standalone import fixturesUtils, os +import mayaUtils +import mayaUsd_createStageWithNewLayer +import mayaUsdDuplicateAsUsdDataOptions import unittest @@ -47,15 +50,25 @@ class ChaserExample1(mayaUsd.lib.ExportChaser): seenChasers = None seenChaserArgs = None + isDuplicating = False + def __init__(self, factoryContext, *args, **kwargs): super(ChaserExample1, self).__init__(factoryContext, *args, **kwargs) jobArgs = factoryContext.GetJobArgs() + self.stage = factoryContext.GetStage() + self.isDuplicating = factoryContext.GetJobArgs().isDuplicating ChaserExample1.seenChasers = jobArgs.chaserNames if ChaserExample1.name in jobArgs.allChaserArgs: ChaserExample1.seenChaserArgs = jobArgs.allChaserArgs[ChaserExample1.name] def ExportDefault(self): ChaserExample1.exportDefaultCalled = True + + if self.isDuplicating: + # creating an extra prim to be tested on Duplicate As + scope = self.stage.DefinePrim("/TestExportDefault", "Scope") + self.RegisterExtraPrimsPaths([scope.GetPath()]) + return self.ExportFrame(Usd.TimeCode.Default()) def ExportFrame(self, frame): @@ -64,6 +77,12 @@ def ExportFrame(self, frame): def PostExport(self): ChaserExample1.postExportCalled = True + + if self.isDuplicating: + # creating an extra prim to be tested on Duplicate As + scope = self.stage.DefinePrim("/TestPostExport", "Scope") + self.RegisterExtraPrimsPaths([scope.GetPath()]) + return True @staticmethod @@ -153,6 +172,7 @@ def ExportFrame(self, frame): def PostExport(self): ChaserExample2.postExportCalled = True + return True @staticmethod @@ -202,6 +222,11 @@ def setUpClass(cls): fixturesUtils.setUpClass(__file__) cls.temp_dir = os.path.abspath('.') + ChaserExample1.register() + ChaserExample2.register() + JobContextExample1.register() + JobContextExample2.register() + @classmethod def tearDownClass(cls): standalone.uninitialize() @@ -210,11 +235,6 @@ def setUp(self): cmds.file(new=True, force=True) def testSimpleExportChaser(self): - ChaserExample1.register() - ChaserExample2.register() - JobContextExample1.register() - JobContextExample2.register() - cmds.polySphere(r = 3.5, name='apple') usdFilePath = os.path.join(self.temp_dir,'testExportChaser.usda') @@ -241,6 +261,25 @@ def testSimpleExportChaser(self): self.assertTrue(ChaserExample2.exportFrameCalled) self.assertTrue(ChaserExample2.postExportCalled) + @unittest.skipUnless(mayaUtils.mayaMajorVersion() >= 2023, 'Requires Maya fixes only available in Maya 2023 or greater.') + def testChaserWithDuplicateAsUsd(self): + sphere = cmds.polySphere(r = 1, name='TestSphere') + + # Create a stage to receive the USD duplicate. + psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + defaultDuplicateAsUsdDataOptions = mayaUsdDuplicateAsUsdDataOptions.getDuplicateAsUsdDataOptionsText() + modifiedDuplicateAsUsdDataOptions = defaultDuplicateAsUsdDataOptions + ";jobContext=[JobContextExample1]" + cmds.mayaUsdDuplicate(cmds.ls(sphere, long=True)[0], psPathStr, exportOptions=modifiedDuplicateAsUsdDataOptions) + + # Check if the extra prims have also been duplicated + stage = mayaUsd.lib.GetPrim(psPathStr).GetStage() + spherePrim = stage.GetPrimAtPath("/TestSphere") + scopeExportPrim = stage.GetPrimAtPath("/TestExportDefault") + scopePostPrim = stage.GetPrimAtPath("/TestPostExport") + + self.assertTrue(spherePrim.IsValid()) + self.assertTrue(scopeExportPrim.IsValid()) + self.assertTrue(scopePostPrim.IsValid()) if __name__ == '__main__': unittest.main(verbosity=2)