Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MAYA-127393 allow USD ref to be relative #2855

Merged
merged 5 commits into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/mayaUsd/resources/scripts/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ list(APPEND scripts_src
mayaUsdLibRegisterStrings.py
mayaUsdAddMayaReference.mel
mayaUsdAddMayaReference.py
mayaUsdAddUSDReference.mel
mayaUsdCacheMayaReference.mel
mayaUsdCacheMayaReference.py
mayaUsdDuplicateAsMayaDataOptions.mel
Expand Down
36 changes: 36 additions & 0 deletions lib/mayaUsd/resources/scripts/mayaUsdAddUSDReference.mel
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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 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 addUSDReferenceToUsdCommitUi(string $parent, string $selectedFile)
{
setParent $parent;
python("import mayaUsd_USDRootFileRelative as murel\nmurel.usdFileRelativeToEditTargetLayer.uiCommit(r'''" + $parent + "''', r'''" + $selectedFile + "''')");
}
113 changes: 84 additions & 29 deletions lib/mayaUsd/ufe/UsdContextOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@
#include <mayaUsd/ufe/UsdUndoAddNewPrimCommand.h>
#include <mayaUsd/ufe/Utils.h>
#include <mayaUsd/utils/util.h>
#include <mayaUsd/utils/utilFileSystem.h>

#include <pxr/base/plug/plugin.h>
#include <pxr/base/plug/registry.h>
#include <pxr/base/tf/diagnostic.h>
#include <pxr/base/tf/stringUtils.h>
#include <pxr/pxr.h>
#include <pxr/usd/sdf/fileFormat.h>
#include <pxr/usd/sdf/path.h>
Expand Down Expand Up @@ -473,58 +475,105 @@ 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 = "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());
}
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 series of file filters, one per particular
// file format. So for N different file formats, we will have N+1 filters.

std::vector<std::string> usdUiStrings;
std::vector<std::string> usdSelectors;
std::vector<std::string> otherUiStrings;
std::vector<std::string> 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
-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()
{
Expand Down Expand Up @@ -1210,9 +1259,15 @@ 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());
if (fileRef.length() == 0)
return nullptr;

std::string path = UsdMayaUtil::convert(fileRef);
const std::string path
= makeUSDReferenceFilePathRelativeIfRequested(UsdMayaUtil::convert(fileRef), prim());
if (path.empty())
return nullptr;

Expand Down
38 changes: 30 additions & 8 deletions lib/mayaUsd/utils/utilFileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,27 +117,41 @@ std::string UsdMayaUtilFileSystem::getMayaSceneFileDir()
return std::string();
}

std::string UsdMayaUtilFileSystem::getPathRelativeToMayaSceneFile(const std::string& fileName)
std::pair<std::string, bool> 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()
Expand All @@ -147,6 +161,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()
{
Expand Down
30 changes: 29 additions & 1 deletion lib/mayaUsd/utils/utilFileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,28 @@ 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 computes a relative path of the first one
to second one.

\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
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<std::string, bool>
makePathRelativeTo(const std::string& fileName, const std::string& relativeToDir);

/*! \brief returns parent directory of a maya scene file opened by reference
*/
MAYAUSD_CORE_PUBLIC
Expand All @@ -55,12 +77,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
Expand Down
4 changes: 3 additions & 1 deletion plugin/adsk/scripts/mayaUSDRegisterStrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +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", "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.")
Loading