From e5b0569efc6e3b9add4f72035d05c0f90e74eb99 Mon Sep 17 00:00:00 2001 From: Pierre Baillargeon Date: Fri, 27 Jan 2023 13:57:52 -0500 Subject: [PATCH 1/5] MAYA-127393 allow USD ref to be relative - Refactor the Python helper to handle the relative-path UI in file dialogs. - Make it more generic by abstracting to which file the path will be relative to, the label of the UI and the option var used. - Add makePathRelativeTo function to UsdMayaUtilFileSystem to make any file path relative to a given directory. - Add requireUsdPathsRelativeToEditTargetLayer function to verify if USD ref paths should be relative. - Make the USD context menu use the new relative ref UI and functions. - Refactor its code a bit to make reading the generated scripts easier. --- lib/mayaUsd/resources/scripts/CMakeLists.txt | 1 + .../scripts/mayaUsdAddUSDReference.mel | 46 ++++++ lib/mayaUsd/ufe/UsdContextOps.cpp | 113 +++++++++---- lib/mayaUsd/utils/utilFileSystem.cpp | 39 ++++- lib/mayaUsd/utils/utilFileSystem.h | 29 +++- plugin/adsk/scripts/mayaUSDRegisterStrings.py | 2 + .../scripts/mayaUsd_USDRootFileRelative.py | 148 ++++++++++++++++-- .../adsk/scripts/mayaUsd_pluginUICreation.mel | 1 + 8 files changed, 324 insertions(+), 55 deletions(-) create mode 100644 lib/mayaUsd/resources/scripts/mayaUsdAddUSDReference.mel diff --git a/lib/mayaUsd/resources/scripts/CMakeLists.txt b/lib/mayaUsd/resources/scripts/CMakeLists.txt index 810d3d2e59..f015db2837 100644 --- a/lib/mayaUsd/resources/scripts/CMakeLists.txt +++ b/lib/mayaUsd/resources/scripts/CMakeLists.txt @@ -3,6 +3,7 @@ list(APPEND scripts_src mayaUsdLibRegisterStrings.py mayaUsdAddMayaReference.mel mayaUsdAddMayaReference.py + mayaUsdAddUSDReference.mel mayaUsdCacheMayaReference.mel mayaUsdCacheMayaReference.py mayaUsdDuplicateAsMayaDataOptions.mel diff --git a/lib/mayaUsd/resources/scripts/mayaUsdAddUSDReference.mel b/lib/mayaUsd/resources/scripts/mayaUsdAddUSDReference.mel new file mode 100644 index 0000000000..16babcf2d5 --- /dev/null +++ b/lib/mayaUsd/resources/scripts/mayaUsdAddUSDReference.mel @@ -0,0 +1,46 @@ +// Copyright 2022 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Unfortunate MEL to Python shims because of fileDialog2 requirements that +// optionsUICreate, optionsUIInit and optionsUICommit2 arguments be MEL +// procedures. + +global proc setUSDReferenceRelativeFilePathRoot(string $filePath) +{ + python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdFileRelativeToEditTargetLayer.setRelativeFilePathRoot(r'''" + $filePath + "''')"); +} + +global proc addUSDReferenceCreateUi(string $parent) +{ + setParent $parent; + string $layout = `scrollLayout -childResizable true`; + python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdFileRelativeToEditTargetLayer.uiCreate(r'''" + $layout + "''')"); +} + +global proc addUSDReferenceInitUi(string $parent, string $filterType) +{ + python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdFileRelativeToEditTargetLayer.uiInit(r'''" + $parent + "''', r'''" + $filterType + "''')"); +} + +global proc addUSDReferenceSelectionChanged(string $parent, string $selection) +{ + // For now, nothing. +} + +global proc addUSDReferenceToUsdCommitUi(string $parent, string $selectedFile) +{ + setParent $parent; + python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdFileRelativeToEditTargetLayer.uiCommit(r'''" + $parent + "''', r'''" + $selectedFile + "''')"); +} diff --git a/lib/mayaUsd/ufe/UsdContextOps.cpp b/lib/mayaUsd/ufe/UsdContextOps.cpp index fe47ee4a06..006153ad39 100644 --- a/lib/mayaUsd/ufe/UsdContextOps.cpp +++ b/lib/mayaUsd/ufe/UsdContextOps.cpp @@ -32,10 +32,12 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -473,58 +475,107 @@ class ToggleInstanceableStateCommand : public Ufe::UndoableCommand bool _instanceable; }; -const char* selectUSDFileScriptPre = R"mel( -global proc string SelectUSDFileForAddReference() +const std::string getTargetLayerFilePath(const UsdPrim& prim) { - string $result[] = `fileDialog2 - -fileMode 1 - -caption "Add Reference to USD Prim" - -fileFilter "USD Files )mel"; + auto stage = prim.GetStage(); + if (!stage) + return {}; -const char* selectUSDFileScriptPost = R"mel("`; + auto layer = stage->GetEditTarget().GetLayer(); + if (!layer) + return {}; - if (0 == size($result)) - return ""; - else - return $result[0]; + return layer->GetRealPath(); +} + +bool _prepareUSDReferenceTargetLayer(const UsdPrim& prim) +{ + const char* script = R"mel( + setUSDReferenceRelativeFilePathRoot("%s"); + )mel"; + + const std::string commandString = TfStringPrintf(script, getTargetLayerFilePath(prim).c_str()); + return MGlobal::executeCommand(commandString.c_str()); } -SelectUSDFileForAddReference(); -)mel"; // Ask SDF for all supported extensions: const char* _selectUSDFileScript() { - static std::string commandString; if (commandString.empty()) { // This is an interactive call from the main UI thread. No need for SMP protections. - commandString = selectUSDFileScriptPre; - std::string usdUiString = "("; - std::string usdSelector = ""; - std::string otherUiString = ""; - std::string otherSelector = ""; + // The goal of the following loop is to build a first file filter that allow any + // USD-compatible file format, then a serie of file filters, one per particular + // file format. So for N different file formats, we will have N+1 filters. + + std::vector usdUiStrings; + std::vector usdSelectors; + std::vector otherUiStrings; + std::vector otherSelectors; for (auto&& extension : SdfFileFormat::FindAllFileFormatExtensions()) { // Put USD first if (extension.rfind("usd", 0) == 0) { - if (!usdSelector.empty()) { - usdUiString += " "; - } - usdUiString += "*." + extension; - usdSelector += ";;*." + extension; + usdUiStrings.push_back("*." + extension); + usdSelectors.push_back("*." + extension); } else { - otherUiString += " *." + extension; - otherSelector += ";;*." + extension; + otherUiStrings.push_back("*." + extension); + otherSelectors.push_back("*." + extension); } } - commandString += usdUiString + otherUiString + ")" + usdSelector + otherSelector - + selectUSDFileScriptPost; + + usdUiStrings.insert(usdUiStrings.end(), otherUiStrings.begin(), otherUiStrings.end()); + usdSelectors.insert(usdSelectors.end(), otherSelectors.begin(), otherSelectors.end()); + + const char* script = R"mel( + global proc string SelectUSDFileForAddReference() + { + string $result[] = `fileDialog2 + -fileMode 1 + -caption "Add Reference to USD Prim" + -fileFilter "USD Files (%s);;%s" + -optionsUICreate addUSDReferenceCreateUi + -optionsUIInit addUSDReferenceInitUi + -selectionChanged addUSDReferenceSelectionChanged + -optionsUICommit2 addUSDReferenceToUsdCommitUi`; + + if (0 == size($result)) + return ""; + else + return $result[0]; + } + SelectUSDFileForAddReference(); + )mel"; + + commandString = TfStringPrintf( + script, TfStringJoin(usdUiStrings).c_str(), TfStringJoin(usdSelectors, ";;").c_str()); } + return commandString.c_str(); } +std::string +makeUSDReferenceFilePathRelativeIfRequested(const std::string& filePath, const UsdPrim& prim) +{ + if (!UsdMayaUtilFileSystem::requireUsdPathsRelativeToEditTargetLayer()) + return filePath; + + const std::string layerDirPath = UsdMayaUtilFileSystem::getDir(getTargetLayerFilePath(prim)); + + auto relativePathAndSuccess = UsdMayaUtilFileSystem::makePathRelativeTo(filePath, layerDirPath); + + if (!relativePathAndSuccess.second) { + TF_WARN( + "File name (%s) cannot be resolved as relative to the current edit target layer, " + "using the absolute path.", + filePath.c_str()); + } + + return relativePathAndSuccess.first; +} + const char* clearAllReferencesConfirmScript = R"( global proc string ClearAllUSDReferencesConfirm() { @@ -1210,9 +1261,13 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) return nullptr; #endif } else if (itemPath[0] == AddUsdReferenceUndoableCommand::commandName) { + if (!_prepareUSDReferenceTargetLayer(prim())) + return nullptr; + MString fileRef = MGlobal::executeCommandStringResult(_selectUSDFileScript()); - std::string path = UsdMayaUtil::convert(fileRef); + const std::string path + = makeUSDReferenceFilePathRelativeIfRequested(UsdMayaUtil::convert(fileRef), prim()); if (path.empty()) return nullptr; diff --git a/lib/mayaUsd/utils/utilFileSystem.cpp b/lib/mayaUsd/utils/utilFileSystem.cpp index ba04f417bd..56e37a967f 100644 --- a/lib/mayaUsd/utils/utilFileSystem.cpp +++ b/lib/mayaUsd/utils/utilFileSystem.cpp @@ -117,27 +117,42 @@ std::string UsdMayaUtilFileSystem::getMayaSceneFileDir() return std::string(); } -std::string UsdMayaUtilFileSystem::getPathRelativeToMayaSceneFile(const std::string& fileName) +std::pair UsdMayaUtilFileSystem::makePathRelativeTo( + const std::string& fileName, + const std::string& relativeToDir) { ghc::filesystem::path absolutePath(fileName); - ghc::filesystem::path basePath(getMayaSceneFileDir()); - // If Maya scene file doesn't exist yet, use the absolute path - if (basePath.empty()) { - return fileName; + // If the anchor relative-to-directory doesn't exist yet, use the unchanged path, + // but don't return a failure. The anchor path being empty is not considered + // a failure. If the caller needs to detect this, they can verify that the + // anchor path is empty themselves before calling this function. + if (relativeToDir.empty()) { + return std::make_pair(fileName, true); } - ghc::filesystem::path relativePath = absolutePath.lexically_relative(basePath); + ghc::filesystem::path relativePath = absolutePath.lexically_relative(relativeToDir); if (relativePath.empty()) { + return std::make_pair(fileName, false); + ; + } + + return std::make_pair(relativePath.generic_string(), true); +} + +std::string UsdMayaUtilFileSystem::getPathRelativeToMayaSceneFile(const std::string& fileName) +{ + auto relativePathAndSuccess = makePathRelativeTo(fileName, getMayaSceneFileDir()); + + if (!relativePathAndSuccess.second) { TF_WARN( "File name (%s) cannot be resolved as relative to the Maya scene file, using the " "absolute path.", fileName.c_str()); - return fileName; } - return relativePath.generic_string(); + return relativePathAndSuccess.first; } bool UsdMayaUtilFileSystem::requireUsdPathsRelativeToMayaSceneFile() @@ -147,6 +162,14 @@ bool UsdMayaUtilFileSystem::requireUsdPathsRelativeToMayaSceneFile() && MGlobal::optionVarIntValue(MAKE_PATH_RELATIVE_TO_SCENE_FILE); } +bool UsdMayaUtilFileSystem::requireUsdPathsRelativeToEditTargetLayer() +{ + static const MString MAKE_PATH_RELATIVE_TO_EDIT_TARGET_LAYER_FILE + = "mayaUsd_MakePathRelativeToEditTargetLayer"; + return MGlobal::optionVarExists(MAKE_PATH_RELATIVE_TO_EDIT_TARGET_LAYER_FILE) + && MGlobal::optionVarIntValue(MAKE_PATH_RELATIVE_TO_EDIT_TARGET_LAYER_FILE); +} + const char* getScenesFolderScript = R"( global proc string UsdMayaUtilFileSystem_GetScenesFolder() { diff --git a/lib/mayaUsd/utils/utilFileSystem.h b/lib/mayaUsd/utils/utilFileSystem.h index e0b5dea28f..793302d636 100644 --- a/lib/mayaUsd/utils/utilFileSystem.h +++ b/lib/mayaUsd/utils/utilFileSystem.h @@ -34,6 +34,27 @@ std::string resolvePath(const std::string& filePath); MAYAUSD_CORE_PUBLIC std::string getDir(const std::string& fullFilePath); +/*! \brief takes in two absolute file paths and returns the first one to second one. + + Also return a boolean that indicates if the attempt to make the file name + relative to the valid anchor path failed. + + If the anchor relative-to-directory is empty, then the original file name + is returned but no failure is returned. If the caller needs to detect + this as a failure case, they can verify that the relative-to directory + name is empty themselves before calling this function. + + The rationale for this is that, for example, we don't want to flag as + an error when the user tries to make a path relative to the scene when + the scene has not yet been saved. + + If the second path is not absolute or is not reachable from the first, + then the returned path will still be absolute. + */ +MAYAUSD_CORE_PUBLIC +std::pair +makePathRelativeTo(const std::string& fileName, const std::string& relativeToDir); + /*! \brief returns parent directory of a maya scene file opened by reference */ MAYAUSD_CORE_PUBLIC @@ -55,12 +76,18 @@ When there is no scene file, the absolute (input) path will be returned. MAYAUSD_CORE_PUBLIC std::string getPathRelativeToMayaSceneFile(const std::string& fileName); -/*! \brief returns the flag specifying whether Usd file paths should be saevd as relative to Maya +/*! \brief returns the flag specifying whether USD file paths should be saved as relative to Maya * scene file */ MAYAUSD_CORE_PUBLIC bool requireUsdPathsRelativeToMayaSceneFile(); +/*! \brief returns the flag specifying whether USD file paths should be saved + * as relative to the current edit target layer. + */ +MAYAUSD_CORE_PUBLIC +bool requireUsdPathsRelativeToEditTargetLayer(); + /*! \brief returns a unique file name */ MAYAUSD_CORE_PUBLIC diff --git a/plugin/adsk/scripts/mayaUSDRegisterStrings.py b/plugin/adsk/scripts/mayaUSDRegisterStrings.py index 502f79acd9..450e8c0427 100644 --- a/plugin/adsk/scripts/mayaUSDRegisterStrings.py +++ b/plugin/adsk/scripts/mayaUSDRegisterStrings.py @@ -37,3 +37,5 @@ def mayaUSDRegisterStrings(): register("kFileOptions", "File Options") register("kMakePathRelativeToSceneFile", "Make Path Relative to Scene File") register("kMakePathRelativeToSceneFileAnn", "If enabled, path will be relative to your Maya scene file. If this option is disabled, there is no Maya scene file and the path will be absolute. Save your Maya scene file to disk to make this option available.") + register("kMakePathRelativeToEditTargetLayer", "Make Path Relative to Edit Target Layer Directory") + register("kMakePathRelativeToEditTargetLayerAnn", "If enabled, path will be relative to the currently targeted layer. If this option is disabled, the target layer has not yet been saved and the path will be absolute. Save the target layer to disk to make this option available.") diff --git a/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py b/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py index 1430b0d538..cd6a6b38a6 100644 --- a/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py +++ b/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py @@ -3,18 +3,50 @@ from mayaUSDRegisterStrings import getMayaUsdString from mayaUsdMayaReferenceUtils import pushOptionsUITemplate -class usdRootFileRelative(object): - fileNameEditField = None +class usdFileRelative(object): + ''' + Helper class to create the UI for load/save dialog boxes that need to make the + selected file name optionally relative. - kMakePathRelativeCheckBox = 'MakePathRelative' + The caller must tell each function to what the file should be made relative. + The 'what' is used to select the correct UI element, UI label and to read + and write the correct option var. + + For example by passing 'SceneFile', the functions will use: + UI element: MakePathRelativeToSceneFile + UI label: kMakePathRelativeToSceneFile + option var: mayaUsd_MakePathRelativeToSceneFile + ''' + + kMakePathRelativeCheckBox = 'MakePathRelativeTo' + + _relativeToFilePath = None @classmethod - def uiCreate(cls, parentLayout): + def setRelativeFilePathRoot(cls, filePath): + ''' + Sets to which file the relative file will be anchored. + Set to empty or None to have the file be absolute, not relative. + ''' + cls._relativeToFilePath = filePath + + @classmethod + def getRelativeFilePathRoot(cls): + ''' + Gets to which file the relative file will be anchored. + Empty or None to have the file be absolute, not relative. + ''' + return cls._relativeToFilePath + + @classmethod + def uiCreate(cls, parentLayout, relativeToWhat): """ - Helper method to create the UI layout for the USD root file relative actions. + Helper method to create the UI layout for the file relative actions. Input parentLayout arg is expected to the a scroll layout into which controls can be added. + + Input relativeToWhat tells what the file is relative to. See the class docs. """ pushOptionsUITemplate() cmds.setParent(parentLayout) @@ -32,32 +64,114 @@ def uiCreate(cls, parentLayout): topForm = cmds.columnLayout('actionOptionsForm', rowSpacing=5) kFileOptionsStr = getMayaUsdString("kFileOptions") - kMakePathRelativeStr = getMayaUsdString("kMakePathRelativeToSceneFile") - kMakePathRelativeAnnStr = getMayaUsdString("kMakePathRelativeToSceneFileAnn") + kMakePathRelativeStr = getMayaUsdString("kMakePathRelativeTo" + relativeToWhat) + kMakePathRelativeAnnStr = getMayaUsdString("kMakePathRelativeTo" + relativeToWhat + "Ann") optBoxMarginWidth = mel.eval('global int $gOptionBoxTemplateDescriptionMarginWidth; $gOptionBoxTemplateDescriptionMarginWidth += 0') cmds.setParent(topForm) cmds.frameLayout(label=kFileOptionsStr, collapsable=False) widgetColumn = cmds.columnLayout() - cmds.checkBox(cls.kMakePathRelativeCheckBox, label=kMakePathRelativeStr, ann=kMakePathRelativeAnnStr) + cmds.checkBox(cls.kMakePathRelativeCheckBox + relativeToWhat, label=kMakePathRelativeStr, ann=kMakePathRelativeAnnStr) @classmethod - def uiInit(cls, parentLayout, filterType): + def uiInit(cls, parentLayout, canBeRelative, relativeToWhat): + """ + Helper method to initialize the UI layout for the file relative actions. + + Input parentLayout arg is expected to the a scroll layout into which controls + can be added. + + Input canBeRelative tells if the file can be made relative at all. If false, + the relative path UI is shown but disabled. + + Input relativeToWhat tells what the file is relative to. See the class docs. + """ cmds.setParent(parentLayout) # Get the current checkbox value from optionVar (if any) and update checkbox. - if cmds.optionVar(exists='mayaUsd_MakePathRelativeToSceneFile'): - relative = cmds.optionVar(query='mayaUsd_MakePathRelativeToSceneFile') - cmds.checkBox(cls.kMakePathRelativeCheckBox, edit=True, value=relative) + if cmds.optionVar(exists='mayaUsd_MakePathRelativeTo' + relativeToWhat): + relative = cmds.optionVar(query='mayaUsd_MakePathRelativeTo' + relativeToWhat) + cmds.checkBox(cls.kMakePathRelativeCheckBox + relativeToWhat, edit=True, value=relative) + # If if cannot be relative, then the checkbox and label should be disabled. + cmds.checkBox(cls.kMakePathRelativeCheckBox + relativeToWhat, edit=True, enable=canBeRelative) + + @classmethod + def uiCommit(cls, parentLayout, relativeToWhat): + """ + Helper method to commit the UI layout for the file relative actions. + + Input parentLayout arg is expected to the a scroll layout into which controls + can be added. + + Input relativeToWhat tells what the file is relative to. See the class docs. + """ + cmds.setParent(parentLayout) + + # Get the current checkbox state and save to optionVar. + relative = cmds.checkBox(cls.kMakePathRelativeCheckBox + relativeToWhat, query=True, value=True) + cmds.optionVar(iv=('mayaUsd_MakePathRelativeTo' + relativeToWhat, relative)) + + +class usdRootFileRelative(usdFileRelative): + ''' + Helper class to create the UI for load/save dialog boxes that need to make the + selected file name optionally relative to the Maya scene file. + ''' + + kRelativeToWhat = 'SceneFile' + + @classmethod + def uiCreate(cls, parentLayout): + usdFileRelative.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. + ''' # If there is no Maya scene file saved, then the checkbox and label should be disabled. haveSceneFile = cmds.file(q=True, exists=True) - cmds.checkBox(cls.kMakePathRelativeCheckBox, edit=True, enable=haveSceneFile) + usdFileRelative.setRelativeFilePathRoot(cmds.file(query=True, sceneName=True)) + usdFileRelative.uiInit(parentLayout, haveSceneFile, cls.kRelativeToWhat) @classmethod def uiCommit(cls, parentLayout, selectedFile=None): - cmds.setParent(parentLayout) + ''' + Note: the function takes an unused selectedFile argument to be compatible + with the dialog2 command API. + ''' + usdFileRelative.uiCommit(parentLayout, cls.kRelativeToWhat) - # Get the current checkbox state and save to optionVar. - relative = cmds.checkBox(cls.kMakePathRelativeCheckBox, query=True, value=True) - cmds.optionVar(iv=('mayaUsd_MakePathRelativeToSceneFile', relative)) + +class usdFileRelativeToEditTargetLayer(usdFileRelative): + ''' + Helper class to create the UI for load/save dialog boxes that need to make the + selected file name optionally relative to a layer file. + ''' + + kRelativeToWhat = 'EditTargetLayer' + + @classmethod + def uiCreate(cls, parentLayout): + usdFileRelative.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. + ''' + # If there is no Maya scene file saved, then the checkbox and label should be disabled. + print(usdFileRelative.getRelativeFilePathRoot()) + canBeRelative = bool(usdFileRelative.getRelativeFilePathRoot()) + usdFileRelative.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. + ''' + usdFileRelative.uiCommit(parentLayout, cls.kRelativeToWhat) diff --git a/plugin/adsk/scripts/mayaUsd_pluginUICreation.mel b/plugin/adsk/scripts/mayaUsd_pluginUICreation.mel index b1fbc5060d..6627ea53d7 100644 --- a/plugin/adsk/scripts/mayaUsd_pluginUICreation.mel +++ b/plugin/adsk/scripts/mayaUsd_pluginUICreation.mel @@ -48,6 +48,7 @@ global proc mayaUsd_pluginUICreation() // is no mechanism to source such a file once, if multiple plugins are // loaded, so source it here in this plugin. PPT, 29-Nov-2021. source "mayaUsdAddMayaReference.mel"; + source "mayaUsdAddUSDReference.mel"; source "mayaUsdCacheMayaReference.mel"; source "mayaUsdMergeToUSDOptions.mel"; source "mayaUsdDuplicateAsMayaDataOptions.mel"; From 26eda691962eeb9b342b5229b3af8f1dc48cc3f7 Mon Sep 17 00:00:00 2001 From: Pierre Baillargeon Date: Mon, 30 Jan 2023 15:49:52 -0500 Subject: [PATCH 2/5] MAYA-127393 improvements - Remove unnecessary MEl scripts. - Call Python directly instead of through MEL. - Fix many comment typos. - Remove debug print statement. --- .../resources/scripts/mayaUsdAddUSDReference.mel | 10 ---------- lib/mayaUsd/ufe/UsdContextOps.cpp | 12 ++++++------ lib/mayaUsd/utils/utilFileSystem.h | 7 ++++--- plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py | 12 ++++++------ 4 files changed, 16 insertions(+), 25 deletions(-) diff --git a/lib/mayaUsd/resources/scripts/mayaUsdAddUSDReference.mel b/lib/mayaUsd/resources/scripts/mayaUsdAddUSDReference.mel index 16babcf2d5..a260d81219 100644 --- a/lib/mayaUsd/resources/scripts/mayaUsdAddUSDReference.mel +++ b/lib/mayaUsd/resources/scripts/mayaUsdAddUSDReference.mel @@ -17,11 +17,6 @@ // optionsUICreate, optionsUIInit and optionsUICommit2 arguments be MEL // procedures. -global proc setUSDReferenceRelativeFilePathRoot(string $filePath) -{ - python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdFileRelativeToEditTargetLayer.setRelativeFilePathRoot(r'''" + $filePath + "''')"); -} - global proc addUSDReferenceCreateUi(string $parent) { setParent $parent; @@ -34,11 +29,6 @@ global proc addUSDReferenceInitUi(string $parent, string $filterType) python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdFileRelativeToEditTargetLayer.uiInit(r'''" + $parent + "''', r'''" + $filterType + "''')"); } -global proc addUSDReferenceSelectionChanged(string $parent, string $selection) -{ - // For now, nothing. -} - global proc addUSDReferenceToUsdCommitUi(string $parent, string $selectedFile) { setParent $parent; diff --git a/lib/mayaUsd/ufe/UsdContextOps.cpp b/lib/mayaUsd/ufe/UsdContextOps.cpp index 006153ad39..822487a5fa 100644 --- a/lib/mayaUsd/ufe/UsdContextOps.cpp +++ b/lib/mayaUsd/ufe/UsdContextOps.cpp @@ -490,12 +490,11 @@ const std::string getTargetLayerFilePath(const UsdPrim& prim) bool _prepareUSDReferenceTargetLayer(const UsdPrim& prim) { - const char* script = R"mel( - setUSDReferenceRelativeFilePathRoot("%s"); - )mel"; + 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::executeCommand(commandString.c_str()); + return MGlobal::executePythonCommand(commandString.c_str()); } // Ask SDF for all supported extensions: @@ -507,7 +506,7 @@ const char* _selectUSDFileScript() // This is an interactive call from the main UI thread. No need for SMP protections. // The goal of the following loop is to build a first file filter that allow any - // USD-compatible file format, then a serie of file filters, one per particular + // USD-compatible file format, then a series of file filters, one per particular // file format. So for N different file formats, we will have N+1 filters. std::vector usdUiStrings; @@ -538,7 +537,6 @@ const char* _selectUSDFileScript() -fileFilter "USD Files (%s);;%s" -optionsUICreate addUSDReferenceCreateUi -optionsUIInit addUSDReferenceInitUi - -selectionChanged addUSDReferenceSelectionChanged -optionsUICommit2 addUSDReferenceToUsdCommitUi`; if (0 == size($result)) @@ -1265,6 +1263,8 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) return nullptr; MString fileRef = MGlobal::executeCommandStringResult(_selectUSDFileScript()); + if (fileRef.isEmpty()) + return nullptr; const std::string path = makeUSDReferenceFilePathRelativeIfRequested(UsdMayaUtil::convert(fileRef), prim()); diff --git a/lib/mayaUsd/utils/utilFileSystem.h b/lib/mayaUsd/utils/utilFileSystem.h index 793302d636..34ed026cc5 100644 --- a/lib/mayaUsd/utils/utilFileSystem.h +++ b/lib/mayaUsd/utils/utilFileSystem.h @@ -34,10 +34,11 @@ std::string resolvePath(const std::string& filePath); MAYAUSD_CORE_PUBLIC std::string getDir(const std::string& fullFilePath); -/*! \brief takes in two absolute file paths and returns the first one to second one. +/*! \brief Takes in two absolute file paths and computes a relative path of the first one + to second one. - Also return a boolean that indicates if the attempt to make the file name - relative to the valid anchor path failed. + \return A pair with the path and a boolean that indicates if the attempt to make the + file name relative to the valid anchor path failed. If the anchor relative-to-directory is empty, then the original file name is returned but no failure is returned. If the caller needs to detect diff --git a/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py b/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py index cd6a6b38a6..60ebf7462a 100644 --- a/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py +++ b/plugin/adsk/scripts/mayaUsd_USDRootFileRelative.py @@ -8,13 +8,14 @@ class usdFileRelative(object): Helper class to create the UI for load/save dialog boxes that need to make the selected file name optionally relative. - The caller must tell each function to what the file should be made relative. - The 'what' is used to select the correct UI element, UI label and to read - and write the correct option var. + The caller must tell each function to what the file should be made relative to. + The 'what' is used to select the correct UI element, UI label, UI tool-tip and + to read and write the correct option var. For example by passing 'SceneFile', the functions will use: UI element: MakePathRelativeToSceneFile UI label: kMakePathRelativeToSceneFile + UI tool-tip: kMakePathRelativeToSceneFileAnn option var: mayaUsd_MakePathRelativeToSceneFile ''' @@ -78,7 +79,7 @@ def uiInit(cls, parentLayout, canBeRelative, relativeToWhat): """ Helper method to initialize the UI layout for the file relative actions. - Input parentLayout arg is expected to the a scroll layout into which controls + Input parentLayout arg is expected to be a scroll layout into which controls can be added. Input canBeRelative tells if the file can be made relative at all. If false, @@ -163,8 +164,7 @@ def uiInit(cls, parentLayout, filterType): Note: the function takes an unused filterType argument to be compatible with the dialog2 command API. ''' - # If there is no Maya scene file saved, then the checkbox and label should be disabled. - print(usdFileRelative.getRelativeFilePathRoot()) + # If there is no target layer saved, then the checkbox and label should be disabled. canBeRelative = bool(usdFileRelative.getRelativeFilePathRoot()) usdFileRelative.uiInit(parentLayout, canBeRelative, cls.kRelativeToWhat) From b05fc57b1d842cd24d6a6add84a46d42d931479f Mon Sep 17 00:00:00 2001 From: Pierre Baillargeon Date: Mon, 30 Jan 2023 15:50:42 -0500 Subject: [PATCH 3/5] MAYA-127393 remove unused semi-column --- lib/mayaUsd/utils/utilFileSystem.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/mayaUsd/utils/utilFileSystem.cpp b/lib/mayaUsd/utils/utilFileSystem.cpp index 56e37a967f..0f5602d8a1 100644 --- a/lib/mayaUsd/utils/utilFileSystem.cpp +++ b/lib/mayaUsd/utils/utilFileSystem.cpp @@ -135,7 +135,6 @@ std::pair UsdMayaUtilFileSystem::makePathRelativeTo( if (relativePath.empty()) { return std::make_pair(fileName, false); - ; } return std::make_pair(relativePath.generic_string(), true); From d2ff14e3473ff2515304c2737acce1ceaf3af883 Mon Sep 17 00:00:00 2001 From: Pierre Baillargeon Date: Tue, 31 Jan 2023 09:33:42 -0500 Subject: [PATCH 4/5] MAYA-127393make compatible with Maya 2022 and older The MString's isEmpty function does not exists in Maya 2022... use the length function instead. --- lib/mayaUsd/ufe/UsdContextOps.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mayaUsd/ufe/UsdContextOps.cpp b/lib/mayaUsd/ufe/UsdContextOps.cpp index 822487a5fa..470e078b9e 100644 --- a/lib/mayaUsd/ufe/UsdContextOps.cpp +++ b/lib/mayaUsd/ufe/UsdContextOps.cpp @@ -1263,7 +1263,7 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) return nullptr; MString fileRef = MGlobal::executeCommandStringResult(_selectUSDFileScript()); - if (fileRef.isEmpty()) + if (fileRef.length() == 0) return nullptr; const std::string path From c6d4d5e00017c9e660ac317de75591cbf18e7eef Mon Sep 17 00:00:00 2001 From: Pierre Baillargeon Date: Tue, 31 Jan 2023 09:58:09 -0500 Subject: [PATCH 5/5] MAYA-127393 make the tool-tip correspond to the designed text. Also, split the tool-tip on multiple lines for readability. --- plugin/adsk/scripts/mayaUSDRegisterStrings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/adsk/scripts/mayaUSDRegisterStrings.py b/plugin/adsk/scripts/mayaUSDRegisterStrings.py index 450e8c0427..ab3c478751 100644 --- a/plugin/adsk/scripts/mayaUSDRegisterStrings.py +++ b/plugin/adsk/scripts/mayaUSDRegisterStrings.py @@ -36,6 +36,6 @@ def mayaUSDRegisterStrings(): register("kLoadUSDFile", "Load USD File") register("kFileOptions", "File Options") register("kMakePathRelativeToSceneFile", "Make Path Relative to Scene File") - register("kMakePathRelativeToSceneFileAnn", "If enabled, path will be relative to your Maya scene file. If this option is disabled, there is no Maya scene file and the path will be absolute. Save your Maya scene file to disk to make this option available.") + 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", "If enabled, path will be relative to the currently targeted layer. If this option is disabled, the target layer has not yet been saved and the path will be absolute. Save the target layer to disk to make this option available.") + 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.")