diff --git a/lib/mayaUsd/nodes/layerManager.cpp b/lib/mayaUsd/nodes/layerManager.cpp index 676cc7ae33..fc63179ec5 100644 --- a/lib/mayaUsd/nodes/layerManager.cpp +++ b/lib/mayaUsd/nodes/layerManager.cpp @@ -816,6 +816,9 @@ void LayerDatabase::convertAnonymousLayers( if (root->IsAnonymous()) { PXR_NS::SdfFileFormat::FileFormatArguments args; std::string newFileName = MayaUsd::utils::generateUniqueFileName(proxyName); + if (UsdMayaUtilFileSystem::requireUsdPathsRelativeToMayaSceneFile()) { + newFileName = UsdMayaUtilFileSystem::getPathRelativeToMayaSceneFile(newFileName); + } MayaUsd::utils::saveLayerWithFormat(root, newFileName); MayaUsd::utils::setNewProxyPath(pShape->name(), UsdMayaUtil::convert(newFileName)); diff --git a/lib/mayaUsd/ufe/UsdContextOps.cpp b/lib/mayaUsd/ufe/UsdContextOps.cpp index de45ff3528..3046cd96e7 100644 --- a/lib/mayaUsd/ufe/UsdContextOps.cpp +++ b/lib/mayaUsd/ufe/UsdContextOps.cpp @@ -481,13 +481,18 @@ class ToggleInstanceableStateCommand : public Ufe::UndoableCommand bool _instanceable; }; -const std::string getTargetLayerFilePath(const UsdPrim& prim) +const PXR_NS::SdfLayerHandle getCurrentTargetLayer(const UsdPrim& prim) { auto stage = prim.GetStage(); if (!stage) return {}; - auto layer = stage->GetEditTarget().GetLayer(); + return stage->GetEditTarget().GetLayer(); +} + +const std::string getTargetLayerFilePath(const UsdPrim& prim) +{ + auto layer = getCurrentTargetLayer(prim); if (!layer) return {}; @@ -496,11 +501,7 @@ const std::string getTargetLayerFilePath(const UsdPrim& prim) bool _prepareUSDReferenceTargetLayer(const UsdPrim& prim) { - const char* script = "import mayaUsd_USDRootFileRelative as murel\n" - "murel.usdFileRelative.setRelativeFilePathRoot(r'''%s''')"; - - const std::string commandString = TfStringPrintf(script, getTargetLayerFilePath(prim).c_str()); - return MGlobal::executePythonCommand(commandString.c_str()); + return UsdMayaUtilFileSystem::prepareLayerSaveUILayer(getCurrentTargetLayer(prim), false); } // Ask SDF for all supported extensions: diff --git a/lib/mayaUsd/utils/utilFileSystem.cpp b/lib/mayaUsd/utils/utilFileSystem.cpp index 0f5602d8a1..4432adc3be 100644 --- a/lib/mayaUsd/utils/utilFileSystem.cpp +++ b/lib/mayaUsd/utils/utilFileSystem.cpp @@ -66,6 +66,31 @@ std::string UsdMayaUtilFileSystem::getDir(const std::string& fullFilePath) return ghc::filesystem::path(fullFilePath).parent_path().string(); } +UsdMayaUtilFileSystem::TemporaryCurrentDir::TemporaryCurrentDir(const std::string& newCurDir) +{ + if (newCurDir.size() > 0) { + _previousCurDir = ghc::filesystem::current_path().string(); + ghc::filesystem::current_path(newCurDir); + } +} + +UsdMayaUtilFileSystem::TemporaryCurrentDir::~TemporaryCurrentDir() +{ + try { + restore(); + } catch (const std::exception&) { + // Ignore exception in destructor. + } +} + +void UsdMayaUtilFileSystem::TemporaryCurrentDir::restore() +{ + if (_previousCurDir.size() > 0) { + ghc::filesystem::current_path(_previousCurDir); + _previousCurDir.clear(); + } +} + std::string UsdMayaUtilFileSystem::getMayaReferencedFileDir(const MObject& proxyShapeNode) { // Can not use MFnDependencyNode(proxyShapeNode).isFromReferencedFile() to test if it is @@ -117,6 +142,15 @@ std::string UsdMayaUtilFileSystem::getMayaSceneFileDir() return std::string(); } +std::string UsdMayaUtilFileSystem::getLayerFileDir(const PXR_NS::SdfLayerHandle& layer) +{ + if (!layer) + return std::string(); + + const std::string layerFileName = layer->GetRealPath(); + return UsdMayaUtilFileSystem::getDir(layerFileName); +} + std::pair UsdMayaUtilFileSystem::makePathRelativeTo( const std::string& fileName, const std::string& relativeToDir) @@ -154,6 +188,45 @@ std::string UsdMayaUtilFileSystem::getPathRelativeToMayaSceneFile(const std::str return relativePathAndSuccess.first; } +std::string UsdMayaUtilFileSystem::getPathRelativeToLayerFile( + const std::string& fileName, + const PXR_NS::SdfLayerHandle& layer) +{ + if (!layer) + return fileName; + + const std::string layerDirPath = getLayerFileDir(layer); + auto relativePathAndSuccess = makePathRelativeTo(fileName, layerDirPath); + + if (!relativePathAndSuccess.second) { + TF_WARN( + "File name (%s) cannot be resolved as relative to its parent layer directory (%s), " + "using the absolute path.", + fileName.c_str(), + layerDirPath.c_str()); + } + + return relativePathAndSuccess.first; +} + +bool UsdMayaUtilFileSystem::prepareLayerSaveUILayer( + const PXR_NS::SdfLayerHandle& layer, + bool useSceneFileForRoot) +{ + std::string layerFileDir; + if (layer) { + layerFileDir = getLayerFileDir(layer); + } else if (useSceneFileForRoot) { + layerFileDir = getMayaSceneFileDir(); + } + + const char* script = "import mayaUsd_USDRootFileRelative as murel\n" + "murel.usdFileRelative.setRelativeFilePathRoot(r'''%s''')"; + + const std::string commandString = TfStringPrintf(script, layerFileDir.c_str()); + return MGlobal::executePythonCommand(commandString.c_str()); +} + bool UsdMayaUtilFileSystem::requireUsdPathsRelativeToMayaSceneFile() { static const MString MAKE_PATH_RELATIVE_TO_SCENE_FILE = "mayaUsd_MakePathRelativeToSceneFile"; @@ -161,6 +234,14 @@ bool UsdMayaUtilFileSystem::requireUsdPathsRelativeToMayaSceneFile() && MGlobal::optionVarIntValue(MAKE_PATH_RELATIVE_TO_SCENE_FILE); } +bool UsdMayaUtilFileSystem::requireUsdPathsRelativeToParentLayer() +{ + static const MString MAKE_PATH_RELATIVE_TO_PARENT_LAYER_FILE + = "mayaUsd_MakePathRelativeToParentLayer"; + return MGlobal::optionVarExists(MAKE_PATH_RELATIVE_TO_PARENT_LAYER_FILE) + && MGlobal::optionVarIntValue(MAKE_PATH_RELATIVE_TO_PARENT_LAYER_FILE); +} + bool UsdMayaUtilFileSystem::requireUsdPathsRelativeToEditTargetLayer() { static const MString MAKE_PATH_RELATIVE_TO_EDIT_TARGET_LAYER_FILE diff --git a/lib/mayaUsd/utils/utilFileSystem.h b/lib/mayaUsd/utils/utilFileSystem.h index 34ed026cc5..5fafdfb2e0 100644 --- a/lib/mayaUsd/utils/utilFileSystem.h +++ b/lib/mayaUsd/utils/utilFileSystem.h @@ -16,6 +16,7 @@ #include #include +#include #include @@ -34,6 +35,26 @@ std::string resolvePath(const std::string& filePath); MAYAUSD_CORE_PUBLIC std::string getDir(const std::string& fullFilePath); +/*! \brief Temporarily change the process current directory. + * If built with an empty string, the current directory will not be changed. + */ +class TemporaryCurrentDir +{ +public: + MAYAUSD_CORE_PUBLIC + TemporaryCurrentDir(const std::string& newCurDir); + + MAYAUSD_CORE_PUBLIC + ~TemporaryCurrentDir(); + + // Restore the previous current directory immediately. + MAYAUSD_CORE_PUBLIC + void restore(); + +private: + std::string _previousCurDir; +}; + /*! \brief Takes in two absolute file paths and computes a relative path of the first one to second one. @@ -66,10 +87,14 @@ std::string getMayaReferencedFileDir(const MObject& proxyShapeNode); MAYAUSD_CORE_PUBLIC std::string getMayaSceneFileDir(); -/*! \brief returns the Maya workspace file rule entry for scenes +/*! \brief returns parent directory of the given layer. */ MAYAUSD_CORE_PUBLIC -std::string getMayaWorkspaceScenesDir(); +std::string getLayerFileDir(const PXR_NS::SdfLayerHandle& layer); + +/*! \brief returns the Maya workspace file rule entry for scenes + */ +MAYAUSD_CORE_PUBLIC std::string getMayaWorkspaceScenesDir(); /*! \brief takes in an absolute file path and returns the path relative to maya scene file. When there is no scene file, the absolute (input) path will be returned. @@ -77,12 +102,33 @@ When there is no scene file, the absolute (input) path will be returned. MAYAUSD_CORE_PUBLIC std::string getPathRelativeToMayaSceneFile(const std::string& fileName); +/*! \brief takes in an absolute file path and returns the path relative to a USD layer. + When there is no layer or the layer has never been saved, then the absolute + (input) path will be returned. + */ +MAYAUSD_CORE_PUBLIC +std::string +getPathRelativeToLayerFile(const std::string& fileName, const PXR_NS::SdfLayerHandle& layer); + /*! \brief returns the flag specifying whether USD file paths should be saved as relative to Maya * scene file */ MAYAUSD_CORE_PUBLIC bool requireUsdPathsRelativeToMayaSceneFile(); +/*! \brief prepares the UI used to save layers with the given layer file path, so that the UI + can potentially make the selected file name relative to that layer. If the layer is + null, the UI can either use the scene file or not make the file relative. + */ +MAYAUSD_CORE_PUBLIC +bool prepareLayerSaveUILayer(const PXR_NS::SdfLayerHandle& layer, bool useSceneFileForRoot); + +/*! \brief returns the flag specifying whether USD file paths should be saved + * as relative to the given parent layer. + */ +MAYAUSD_CORE_PUBLIC +bool requireUsdPathsRelativeToParentLayer(); + /*! \brief returns the flag specifying whether USD file paths should be saved * as relative to the current edit target layer. */ diff --git a/lib/mayaUsd/utils/utilSerialization.cpp b/lib/mayaUsd/utils/utilSerialization.cpp index a17b4d754b..f0650d416e 100644 --- a/lib/mayaUsd/utils/utilSerialization.cpp +++ b/lib/mayaUsd/utils/utilSerialization.cpp @@ -302,11 +302,30 @@ SdfLayerRefPtr saveAnonymousLayer( saveLayerWithFormat(anonLayer, filePath, formatArg); - SdfLayerRefPtr newLayer = SdfLayer::FindOrOpen(filePath); + const bool isSubLayer = (parent._layerParent != nullptr); + std::string relativePathAnchor; + + if (savePathAsRelative) { + if (isSubLayer) { + filePath + = UsdMayaUtilFileSystem::getPathRelativeToLayerFile(filePath, parent._layerParent); + relativePathAnchor = UsdMayaUtilFileSystem::getLayerFileDir(parent._layerParent); + } else { + filePath = UsdMayaUtilFileSystem::getPathRelativeToMayaSceneFile(filePath); + relativePathAnchor = UsdMayaUtilFileSystem::getMayaSceneFileDir(); + } + } + + // When the filePath was made relative, we need to help FindOrOpen to locate + // the sub-layers when using relative paths. We temporarily chande the + // current directory to the location the file path is relative to. + UsdMayaUtilFileSystem::TemporaryCurrentDir tempCurDir(relativePathAnchor); + SdfLayerRefPtr newLayer = SdfLayer::FindOrOpen(filePath); + tempCurDir.restore(); + if (newLayer) { - if (parent._layerParent) { - parent._layerParent->GetSubLayerPaths().Replace( - anonLayer->GetIdentifier(), newLayer->GetIdentifier()); + if (isSubLayer) { + updateSubLayer(parent._layerParent, anonLayer, filePath); } else if (!parent._proxyPath.empty()) { saveRootLayer(newLayer, parent._proxyPath, savePathAsRelative); } @@ -318,6 +337,31 @@ SdfLayerRefPtr saveAnonymousLayer( return newLayer; } +void updateSubLayer( + const SdfLayerRefPtr& parentLayer, + const SdfLayerRefPtr& oldSubLayer, + const std::string& newSubLayerPath) +{ + if (!parentLayer) + return; + + if (!oldSubLayer) + return; + + // Note: we don't know if the old sub-layer was referenced with an absolute + // or relative path, so we try replacing both and its identifier. + SdfSubLayerProxy subLayers = parentLayer->GetSubLayerPaths(); + + subLayers.Replace(oldSubLayer->GetIdentifier(), newSubLayerPath); + + const std::string oldAbsPath = oldSubLayer->GetRealPath(); + subLayers.Replace(oldAbsPath, newSubLayerPath); + + const std::string oldRelPath + = UsdMayaUtilFileSystem::getPathRelativeToLayerFile(oldAbsPath, parentLayer); + subLayers.Replace(oldRelPath, newSubLayerPath); +} + void ensureUSDFileExtension(std::string& filePath) { const std::string& extension = SdfFileFormat::GetFileExtension(filePath); diff --git a/lib/mayaUsd/utils/utilSerialization.h b/lib/mayaUsd/utils/utilSerialization.h index 2b6cebe7ae..6501e16950 100644 --- a/lib/mayaUsd/utils/utilSerialization.h +++ b/lib/mayaUsd/utils/utilSerialization.h @@ -95,6 +95,10 @@ struct StageLayersToSave If the file path is empty then use the current file path of the layer. If the format is empty then use the current user-selected USD format option as defined by the usdFormatArgOption() function. (See above.) + + If the file path is relative, then it can be made relative to either the scene + file (for the root layer) or its parent layer (for sub-layers). We assume the + caller voluntarily made the path relative. */ MAYAUSD_CORE_PUBLIC bool saveLayerWithFormat( @@ -125,6 +129,18 @@ PXR_NS::SdfLayerRefPtr saveAnonymousLayer( LayerParent parent, std::string formatArg = ""); +/*! \brief Update the list of sub-layers with a new layer identity. + * The new sub-layer is identified by its path explicitly, + * because a given layer might get referenced through multiple + * different relative paths, so we cannot interrogate it about + * what its path is. + */ +MAYAUSD_CORE_PUBLIC +void updateSubLayer( + const SdfLayerRefPtr& parentLayer, + const SdfLayerRefPtr& oldSubLayer, + const std::string& newSubLayerPath); + /*! \brief Ensures that the filepath contains a valid USD extension. */ MAYAUSD_CORE_PUBLIC diff --git a/lib/usd/ui/layerEditor/layerTreeItem.cpp b/lib/usd/ui/layerEditor/layerTreeItem.cpp index ecb7fb57d7..c67305a314 100644 --- a/lib/usd/ui/layerEditor/layerTreeItem.cpp +++ b/lib/usd/ui/layerEditor/layerTreeItem.cpp @@ -362,7 +362,7 @@ void LayerTreeItem::saveAnonymousLayer() auto sessionState = parentModel()->sessionState(); std::string fileName; - if (sessionState->saveLayerUI(nullptr, &fileName)) { + if (sessionState->saveLayerUI(nullptr, &fileName, parentLayer())) { MayaUsd::utils::ensureUSDFileExtension(fileName); @@ -370,23 +370,37 @@ void LayerTreeItem::saveAnonymousLayer() const QString dialogTitle = StringResources::getAsQString(StringResources::kSaveLayer); std::string formatTag = MayaUsd::utils::usdFormatArgOption(); if (saveSubLayer(dialogTitle, parentLayerItem(), layer(), fileName, formatTag)) { - printf("USD Layer written to %s\n", fileName.c_str()); // now replace the layer in the parent if (isRootLayer()) { - sessionState->rootLayerPathChanged( - UsdMayaUtilFileSystem::requireUsdPathsRelativeToMayaSceneFile() - ? UsdMayaUtilFileSystem::getPathRelativeToMayaSceneFile(fileName) - : fileName); + if (UsdMayaUtilFileSystem::requireUsdPathsRelativeToMayaSceneFile()) { + fileName = UsdMayaUtilFileSystem::getPathRelativeToMayaSceneFile(fileName); + } + sessionState->rootLayerPathChanged(fileName); } else { - // now replace the layer in the parent auto parentItem = parentLayerItem(); + + std::string relativePathAnchor; + if (parentItem && UsdMayaUtilFileSystem::requireUsdPathsRelativeToParentLayer()) { + fileName = UsdMayaUtilFileSystem::getPathRelativeToLayerFile( + fileName, parentItem->layer()); + relativePathAnchor + = UsdMayaUtilFileSystem::getLayerFileDir(parentItem->layer()); + } + + // Now replace the layer in the parent + // + // When the filePath was made relative, we need to help FindOrOpen to locate + // the sub-layers when using relative paths. We temporarily chande the + // current directory to the location the file path is relative to. + UsdMayaUtilFileSystem::TemporaryCurrentDir tempCurDir(relativePathAnchor); auto newLayer = SdfLayer::FindOrOpen(fileName); + tempCurDir.restore(); + if (newLayer) { bool setTarget = _isTargetLayer; auto model = parentModel(); - parentItem->layer()->GetSubLayerPaths().Replace( - layer()->GetIdentifier(), newLayer->GetIdentifier()); + MayaUsd::utils::updateSubLayer(parentItem->layer(), layer(), fileName); if (setTarget) { sessionState->stage()->SetEditTarget(newLayer); } diff --git a/lib/usd/ui/layerEditor/layerTreeItem.h b/lib/usd/ui/layerEditor/layerTreeItem.h index 9c48e14c6f..e739792f57 100644 --- a/lib/usd/ui/layerEditor/layerTreeItem.h +++ b/lib/usd/ui/layerEditor/layerTreeItem.h @@ -124,6 +124,11 @@ class LayerTreeItem : public QStandardItem bool isAnonymous() const { return _layer ? _layer->IsAnonymous() : false; } bool isRootLayer() const { return _layerType == LayerType::RootLayer; } PXR_NS::SdfLayerRefPtr layer() const { return _layer; } + PXR_NS::SdfLayerRefPtr parentLayer() const + { + auto parentItem = parentLayerItem(); + return parentItem ? parentItem->layer() : nullptr; + } // allows c++ iteration of children LayerItemVector childrenVector() const; diff --git a/lib/usd/ui/layerEditor/mayaSessionState.cpp b/lib/usd/ui/layerEditor/mayaSessionState.cpp index d344fefa86..804848c92e 100644 --- a/lib/usd/ui/layerEditor/mayaSessionState.cpp +++ b/lib/usd/ui/layerEditor/mayaSessionState.cpp @@ -272,9 +272,12 @@ void MayaSessionState::sceneClosingCB(void* clientData) Q_EMIT THIS->clearUIOnSceneResetSignal(); } -bool MayaSessionState::saveLayerUI(QWidget* in_parent, std::string* out_filePath) const +bool MayaSessionState::saveLayerUI( + QWidget* in_parent, + std::string* out_filePath, + const PXR_NS::SdfLayerRefPtr& parentLayer) const { - return SaveLayersDialog::saveLayerFilePathUI(*out_filePath); + return SaveLayersDialog::saveLayerFilePathUI(*out_filePath, parentLayer); } std::vector diff --git a/lib/usd/ui/layerEditor/mayaSessionState.h b/lib/usd/ui/layerEditor/mayaSessionState.h index e213b0ef3a..6612dddb8c 100644 --- a/lib/usd/ui/layerEditor/mayaSessionState.h +++ b/lib/usd/ui/layerEditor/mayaSessionState.h @@ -60,7 +60,10 @@ class MayaSessionState std::vector loadLayersUI(const QString& title, const std::string& default_path) const override; // ui to save a layer. returns the path - bool saveLayerUI(QWidget* in_parent, std::string* out_filePath) const override; + bool saveLayerUI( + QWidget* in_parent, + std::string* out_filePath, + const PXR_NS::SdfLayerRefPtr& parentLayer) const override; void printLayer(const PXR_NS::SdfLayerRefPtr& layer) const override; // main API diff --git a/lib/usd/ui/layerEditor/saveLayersDialog.cpp b/lib/usd/ui/layerEditor/saveLayersDialog.cpp index 943d91a1bb..a769e4a4cb 100644 --- a/lib/usd/ui/layerEditor/saveLayersDialog.cpp +++ b/lib/usd/ui/layerEditor/saveLayersDialog.cpp @@ -176,9 +176,16 @@ void SaveLayerPathRow::setPathToSaveAs(const std::string& path) void SaveLayerPathRow::onOpenBrowser() { std::string fileName; - if (SaveLayersDialog::saveLayerFilePathUI(fileName)) { - if (UsdMayaUtilFileSystem::requireUsdPathsRelativeToMayaSceneFile()) { - fileName = UsdMayaUtilFileSystem::getPathRelativeToMayaSceneFile(fileName); + if (SaveLayersDialog::saveLayerFilePathUI(fileName, _layerInfo.parent._layerParent)) { + if (_layerInfo.parent._layerParent) { + if (UsdMayaUtilFileSystem::requireUsdPathsRelativeToParentLayer()) { + fileName = UsdMayaUtilFileSystem::getPathRelativeToLayerFile( + fileName, _layerInfo.parent._layerParent); + } + } else { + if (UsdMayaUtilFileSystem::requireUsdPathsRelativeToMayaSceneFile()) { + fileName = UsdMayaUtilFileSystem::getPathRelativeToMayaSceneFile(fileName); + } } setPathToSaveAs(fileName); @@ -587,11 +594,19 @@ bool SaveLayersDialog::okToSave() } /*static*/ -bool SaveLayersDialog::saveLayerFilePathUI(std::string& out_filePath) +bool SaveLayersDialog::saveLayerFilePathUI( + std::string& out_filePath, + const PXR_NS::SdfLayerRefPtr& parentLayer) { + const bool useSceneFileForRoot = true; + UsdMayaUtilFileSystem::prepareLayerSaveUILayer(parentLayer, useSceneFileForRoot); + + MString cmd; + cmd.format("UsdLayerEditor_SaveLayerFileDialog(^1s)", parentLayer ? "0" : "1"); + MString fileSelected; MGlobal::executeCommand( - MString("UsdLayerEditor_SaveLayerFileDialog"), + cmd, fileSelected, /*display*/ true, /*undo*/ false); diff --git a/lib/usd/ui/layerEditor/saveLayersDialog.h b/lib/usd/ui/layerEditor/saveLayersDialog.h index 3dd8892e60..4753308466 100644 --- a/lib/usd/ui/layerEditor/saveLayersDialog.h +++ b/lib/usd/ui/layerEditor/saveLayersDialog.h @@ -42,7 +42,8 @@ class SaveLayersDialog : public QDialog // UI to get a file path to save a layer. // As output returns the path. - static bool saveLayerFilePathUI(std::string& out_filePath); + static bool + saveLayerFilePathUI(std::string& out_filePath, const PXR_NS::SdfLayerRefPtr& parentLayer); protected: void onSaveAll(); diff --git a/lib/usd/ui/layerEditor/sessionState.h b/lib/usd/ui/layerEditor/sessionState.h index e6c46713e3..4fc1c34567 100644 --- a/lib/usd/ui/layerEditor/sessionState.h +++ b/lib/usd/ui/layerEditor/sessionState.h @@ -90,7 +90,10 @@ class SessionState : public QObject virtual std::vector loadLayersUI(const QString& title, const std::string& default_path) const = 0; // ui to save a layer. returns the path - virtual bool saveLayerUI(QWidget* in_parent, std::string* out_filePath) const = 0; + virtual bool saveLayerUI( + QWidget* in_parent, + std::string* out_filePath, + const PXR_NS::SdfLayerRefPtr& parentLayer) const = 0; virtual void printLayer(const PXR_NS::SdfLayerRefPtr& layer) const = 0; // main API diff --git a/plugin/adsk/scripts/mayaUSDRegisterStrings.py b/plugin/adsk/scripts/mayaUSDRegisterStrings.py index 94312d3176..546fcb57c8 100644 --- a/plugin/adsk/scripts/mayaUSDRegisterStrings.py +++ b/plugin/adsk/scripts/mayaUSDRegisterStrings.py @@ -39,6 +39,8 @@ def mayaUSDRegisterStrings(): register("kMakePathRelativeToSceneFileAnn", "If enabled, path will be relative to your Maya scene file.\nIf this option is disabled, there is no Maya scene file and the path will be absolute.\nSave your Maya scene file to disk to make this option available.") register("kMakePathRelativeToEditTargetLayer", "Make Path Relative to Edit Target Layer Directory") register("kMakePathRelativeToEditTargetLayerAnn", "Enable to activate relative pathing to your current edit target layer’s directory.\nIf this option is disabled, verify that your target layer is not anonymous and save it to disk.") + register("kMakePathRelativeToParentLayer", "Make Path Relative to Parent Layer Directory") + register("kMakePathRelativeToParentLayerAnn", "Enable to activate relative pathing to your current parent layer’s directory.\nIf this option is disabled, verify that your parent layer is not anonymous and save it to disk.") register("kUnresolvedPath", "Unresolved Path:") register("kUnresolvedPathAnn", "This field indicates the path with the file name currently chosen in your text input. Note: This is the string that will be written out to the file in the chosen directory in order to enable portability.") register("kResolvedPath", "Resolved Path:") diff --git a/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py b/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py index 2351373cc2..244e1e4252 100644 --- a/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py +++ b/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py @@ -103,11 +103,11 @@ def uiCreate(cls, parentLayout, relativeToWhat): cmds.frameLayout(label=kFileOptionsStr, collapsable=False) widgetColumn = cmds.columnLayout() cmds.checkBox(cls.kMakePathRelativeCheckBox, label=kMakePathRelativeStr, ann=kMakePathRelativeAnnStr) - if relativeToWhat == 'SceneFile': - cmds.checkBox(cls.kMakePathRelativeCheckBox, edit=True, changeCommand=cls.onMakePathRelativeChanged) - cmds.textFieldGrp(cls.kUnresolvedPathTextField, label=kUnresolvedPathStr, ann=kUnresolvedPathAnnStr, editable=False) - cmds.textFieldGrp(cls.kResolvedPathTextField, label=kResolvedPathStr, ann=kResolvedPathAnnStr , editable=False) - cls._haveRelativePathFields = True + + cmds.checkBox(cls.kMakePathRelativeCheckBox, edit=True, changeCommand=cls.onMakePathRelativeChanged) + cmds.textFieldGrp(cls.kUnresolvedPathTextField, label=kUnresolvedPathStr, ann=kUnresolvedPathAnnStr, editable=False) + cmds.textFieldGrp(cls.kResolvedPathTextField, label=kResolvedPathStr, ann=kResolvedPathAnnStr , editable=False) + cls._haveRelativePathFields = True @classmethod def uiInit(cls, parentLayout, canBeRelative, relativeToWhat): @@ -288,6 +288,36 @@ def uiCommit(cls, parentLayout, selectedFile=None): super(usdRootFileRelative, cls).uiCommit(parentLayout, cls.kRelativeToWhat) +class usdSubLayerFileRelative(usdFileRelative): + ''' + Helper class to create the UI for load/save dialog boxes that need to make the + selected file name optionally relative to a parent layer. + ''' + + kRelativeToWhat = 'ParentLayer' + + @classmethod + def uiCreate(cls, parentLayout): + super(usdSubLayerFileRelative, cls).uiCreate(parentLayout, cls.kRelativeToWhat) + + @classmethod + def uiInit(cls, parentLayout, filterType): + ''' + Note: the function takes an unused filterType argument to be compatible + with the dialog2 command API. + ''' + canBeRelative = bool(usdFileRelative.getRelativeFilePathRoot()) + super(usdSubLayerFileRelative, cls).uiInit(parentLayout, canBeRelative, cls.kRelativeToWhat) + + @classmethod + def uiCommit(cls, parentLayout, selectedFile=None): + ''' + Note: the function takes an unused selectedFile argument to be compatible + with the dialog2 command API. + ''' + super(usdSubLayerFileRelative, cls).uiCommit(parentLayout, cls.kRelativeToWhat) + + class usdFileRelativeToEditTargetLayer(usdFileRelative): ''' Helper class to create the UI for load/save dialog boxes that need to make the diff --git a/plugin/adsk/scripts/mayaUsd_layerEditorFileDialogs.mel b/plugin/adsk/scripts/mayaUsd_layerEditorFileDialogs.mel index fb7fe83151..7810d127b3 100644 --- a/plugin/adsk/scripts/mayaUsd_layerEditorFileDialogs.mel +++ b/plugin/adsk/scripts/mayaUsd_layerEditorFileDialogs.mel @@ -13,6 +13,10 @@ // limitations under the License. // +/////////////////////////////////////////////////////////// +// +// Root-relative callbacks + global proc mayaUsd_USDRootFileRelative_UICreate(string $parent) { // First create the scroll layout here and then call the python @@ -42,7 +46,44 @@ global proc mayaUsd_USDRootFileRelative_FileTypeChanged(string $parent, string $ python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdRootFileRelative.fileTypeChanged('" + $parent + "', '" + $newType + "')"); } -global proc string UsdLayerEditor_SaveLayerFileDialog() +/////////////////////////////////////////////////////////// +// +// Sub-layer-relative callbacks + +global proc mayaUsd_USDSubLayerFileRelative_UICreate(string $parent) +{ + // First create the scroll layout here and then call the python + // helper to add the rest of the UI. + setParent $parent; + string $layout = `scrollLayout -childResizable true`; + python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdSubLayerFileRelative.uiCreate('" + $layout + "')"); +} + +global proc mayaUsd_USDSubLayerFileRelative_UIInit(string $parent, string $filterType) +{ + python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdSubLayerFileRelative.uiInit('" + $parent + "', '" + $filterType + "')"); +} + +global proc mayaUsd_USDSubLayerFileRelative_UICommit(string $parent, string $selectedFile) +{ + python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdSubLayerFileRelative.uiCommit('" + $parent + "', '" + $selectedFile + "')"); +} + +global proc mayaUsd_USDSubLayerFileRelative_SelectionChanged(string $parent, string $selection) +{ + python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdSubLayerFileRelative.selectionChanged('" + $parent + "', '" + $selection + "')"); +} + +global proc mayaUsd_USDSubLayerFileRelative_FileTypeChanged(string $parent, string $newType) +{ + python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdSubLayerFileRelative.fileTypeChanged('" + $parent + "', '" + $newType + "')"); +} + +/////////////////////////////////////////////////////////// +// +// File dialog + +global proc string UsdLayerEditor_SaveLayerFileDialog(int $isRootLayer) { string $fileFilter = python("from mayaUsdUtils import getUSDDialogFileFilters; getUSDDialogFileFilters(False)"); @@ -52,14 +93,33 @@ global proc string UsdLayerEditor_SaveLayerFileDialog() $startDir = dirname($fullPath); } + string $createCallback; + string $initCallback; + string $commitCallback; + string $fileTypeCallback; + string $selectionCallback; + if ($isRootLayer) { + $createCallback = "mayaUsd_USDRootFileRelative_UICreate"; + $initCallback = "mayaUsd_USDRootFileRelative_UIInit"; + $commitCallback = "mayaUsd_USDRootFileRelative_UICommit"; + $fileTypeCallback = "mayaUsd_USDRootFileRelative_FileTypeChanged"; + $selectionCallback = "mayaUsd_USDRootFileRelative_SelectionChanged"; + } else { + $createCallback = "mayaUsd_USDSubLayerFileRelative_UICreate"; + $initCallback = "mayaUsd_USDSubLayerFileRelative_UIInit"; + $commitCallback = "mayaUsd_USDSubLayerFileRelative_UICommit"; + $fileTypeCallback = "mayaUsd_USDSubLayerFileRelative_FileTypeChanged"; + $selectionCallback = "mayaUsd_USDSubLayerFileRelative_SelectionChanged"; + } + string $result[] = `fileDialog2 -fileMode 0 -fileFilter $fileFilter - -optionsUICreate "mayaUsd_USDRootFileRelative_UICreate" - -optionsUIInit "mayaUsd_USDRootFileRelative_UIInit" - -optionsUICommit2 "mayaUsd_USDRootFileRelative_UICommit" - -fileTypeChanged "mayaUsd_USDRootFileRelative_FileTypeChanged" - -selectionChanged "mayaUsd_USDRootFileRelative_SelectionChanged" + -optionsUICreate $createCallback + -optionsUIInit $initCallback + -optionsUICommit2 $commitCallback + -fileTypeChanged $fileTypeCallback + -selectionChanged $selectionCallback -startingDirectory $startDir `;