diff --git a/lib/mayaUsd/commands/baseExportCommand.cpp b/lib/mayaUsd/commands/baseExportCommand.cpp index c700b946c7..a0923a9ebb 100644 --- a/lib/mayaUsd/commands/baseExportCommand.cpp +++ b/lib/mayaUsd/commands/baseExportCommand.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -332,11 +333,33 @@ MStatus MayaUSDExportCommand::doIt(const MArgList& args) frameSamples.insert(tmpArgList.asDouble(0)); } + // The priority order for what objects get exported is (from highest to lowest): + // + // - Requesting to export the current selection. + // - Explicit export roots provided to the command. + // - Explicit objects given to the command. + // - Otherwise defaults to all objects. + // + // This priority order is embodied from code here and from code in the function + // UsdMayaUtil::GetFilteredSelectionToExport. + MSelectionList objSelList; UsdMayaUtil::MDagPathSet dagPaths; bool exportSelected = argData.isFlagSet(kSelectionFlag); if (!exportSelected) { - argData.getObjects(objSelList); + if (userArgs.count(UsdMayaJobExportArgsTokens->exportRoots) > 0) { + const auto exportRoots = DictUtils::extractVector( + userArgs, UsdMayaJobExportArgsTokens->exportRoots); + if (exportRoots.size() > 0) { + for (const std::string& root : exportRoots) { + objSelList.add(root.c_str()); + } + } + } + + if (objSelList.isEmpty()) { + argData.getObjects(objSelList); + } } UsdMayaUtil::GetFilteredSelectionToExport(exportSelected, objSelList, dagPaths); diff --git a/lib/mayaUsd/fileio/jobs/jobArgs.cpp b/lib/mayaUsd/fileio/jobs/jobArgs.cpp index cd71167d26..a073788ca5 100644 --- a/lib/mayaUsd/fileio/jobs/jobArgs.cpp +++ b/lib/mayaUsd/fileio/jobs/jobArgs.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -75,159 +76,8 @@ TF_DEFINE_PRIVATE_TOKENS( // clang-format on namespace { -/// Extracts a bool at \p key from \p userArgs, or false if it can't extract. -bool _Boolean(const VtDictionary& userArgs, const TfToken& key) -{ - if (!VtDictionaryIsHolding(userArgs, key)) { - TF_CODING_ERROR( - "Dictionary is missing required key '%s' or key is " - "not bool type", - key.GetText()); - return false; - } - return VtDictionaryGet(userArgs, key); -} - -/// Extracts a pointer at \p key from \p userArgs, or nullptr if it can't extract. -UsdStageRefPtr _UsdStageRefPtr(const VtDictionary& userArgs, const TfToken& key) -{ - if (!VtDictionaryIsHolding(userArgs, key)) { - TF_CODING_ERROR( - "Dictionary is missing required key '%s' or key is " - "not pointer type", - key.GetText()); - return nullptr; - } - return VtDictionaryGet(userArgs, key); -} - -/// Extracts a double at \p key from \p userArgs, or defaultValue if it can't extract. -double _Double(const VtDictionary& userArgs, const TfToken& key, double defaultValue) -{ - if (VtDictionaryIsHolding(userArgs, key)) - return VtDictionaryGet(userArgs, key); - - // Since user dictionary can be provided from Python and in Python it is easy to - // mix int and double, especially since value literal will take the simplest value - // they can, for example 0 will be an int, support receiving the value as an integer. - if (VtDictionaryIsHolding(userArgs, key)) - return VtDictionaryGet(userArgs, key); - - TF_CODING_ERROR( - "Dictionary is missing required key '%s' or key is " - "not double type", - key.GetText()); - return defaultValue; -} - -/// Extracts a string at \p key from \p userArgs, or "" if it can't extract. -std::string _String(const VtDictionary& userArgs, const TfToken& key) -{ - if (!VtDictionaryIsHolding(userArgs, key)) { - TF_CODING_ERROR( - "Dictionary is missing required key '%s' or key is " - "not string type", - key.GetText()); - return std::string(); - } - return VtDictionaryGet(userArgs, key); -} - -/// Extracts a token at \p key from \p userArgs. -/// If the token value is not either \p defaultToken or one of the -/// \p otherTokens, then returns \p defaultToken instead. -TfToken _Token( - const VtDictionary& userArgs, - const TfToken& key, - const TfToken& defaultToken, - const std::vector& otherTokens) -{ - const TfToken tok(_String(userArgs, key)); - for (const TfToken& allowedTok : otherTokens) { - if (tok == allowedTok) { - return tok; - } - } - // Empty token will silently be promoted to default value. - // Warning for non-empty tokens that don't match. - if (tok != defaultToken && !tok.IsEmpty()) { - TF_WARN( - "Value '%s' is not allowed for flag '%s'; using fallback '%s' " - "instead", - tok.GetText(), - key.GetText(), - defaultToken.GetText()); - } - return defaultToken; -} - -/// Extracts an absolute path at \p key from \p userArgs, or the empty path if -/// it can't extract. -SdfPath _AbsolutePath(const VtDictionary& userArgs, const TfToken& key) -{ - const std::string s = _String(userArgs, key); - // Assume that empty strings are empty paths. (This might be an error case.) - if (s.empty()) { - return SdfPath(); - } - // Make all relative paths into absolute paths. - SdfPath path(s); - if (path.IsAbsolutePath()) { - return path; - } else { - return SdfPath::AbsoluteRootPath().AppendPath(path); - } -} - -/// Extracts an vector from the vector at \p key in \p userArgs. -/// Returns an empty vector if it can't convert the entire value at \p key into -/// a vector. -template std::vector _Vector(const VtDictionary& userArgs, const TfToken& key) -{ - // Check that vector exists. - if (VtDictionaryIsHolding>(userArgs, key)) { - std::vector vals = VtDictionaryGet>(userArgs, key); - return vals; - } - - if (!VtDictionaryIsHolding>(userArgs, key)) { - TF_CODING_ERROR( - "Dictionary is missing required key '%s' or key is " - "not vector type", - key.GetText()); - return std::vector(); - } - - // Check that vector is correctly-typed. - std::vector vals = VtDictionaryGet>(userArgs, key); - if (!std::all_of(vals.begin(), vals.end(), [](const VtValue& v) { return v.IsHolding(); })) { - TF_CODING_ERROR( - "Vector at dictionary key '%s' contains elements of " - "the wrong type", - key.GetText()); - return std::vector(); - } - - // Extract values. - std::vector result; - for (const VtValue& v : vals) { - result.push_back(v.UncheckedGet()); - } - return result; -} - -/// Convenience function that takes the result of _Vector and converts it to a -/// TfToken::Set. -TfToken::Set _TokenSet(const VtDictionary& userArgs, const TfToken& key) -{ - const std::vector vec = _Vector(userArgs, key); - TfToken::Set result; - for (const std::string& s : vec) { - result.insert(TfToken(s)); - } - return result; -} +using namespace MayaUsd::DictUtils; // The chaser args are stored as vectors of vectors (since this is how you // would need to pass them in the Maya Python command API). Convert this to a @@ -236,7 +86,7 @@ std::map _ChaserArgs(const VtDictionary& userArgs, const TfToken& key) { const std::vector> chaserArgs - = _Vector>(userArgs, key); + = extractVector>(userArgs, key); std::map result; for (const std::vector& argTriple : chaserArgs) { @@ -255,7 +105,8 @@ _ChaserArgs(const VtDictionary& userArgs, const TfToken& key) std::map _UVSetRemaps(const VtDictionary& userArgs, const TfToken& key) { - const std::vector> uvRemaps = _Vector>(userArgs, key); + const std::vector> uvRemaps + = extractVector>(userArgs, key); std::map result; for (const std::vector& remap : uvRemaps) { @@ -277,7 +128,7 @@ UsdMayaJobImportArgs::ShadingModes _shadingModesImportArgs(const VtDictionary& userArgs, const TfToken& key) { const std::vector> shadingModeArgs - = _Vector>(userArgs, key); + = extractVector>(userArgs, key); const TfTokenVector modes = UsdMayaShadingModeRegistry::ListImporters(); @@ -365,7 +216,7 @@ PcpMapFunction::PathMap _ExportRootsMap( bool includeEntireSelection = false; - const std::vector exportRoots = _Vector(userArgs, key); + const std::vector exportRoots = extractVector(userArgs, key); for (const std::string& rootPath : exportRoots) { if (!rootPath.empty()) { MDagPath rootDagPath = UsdMayaUtil::nameToDagPath(rootPath); @@ -425,7 +276,7 @@ void _AddFilteredTypeName(const MString& typeName, std::set& filte std::set _FilteredTypeIds(const VtDictionary& userArgs) { const std::vector vec - = _Vector(userArgs, UsdMayaJobExportArgsTokens->filterTypes); + = extractVector(userArgs, UsdMayaJobExportArgsTokens->filterTypes); std::set result; for (const std::string& s : vec) { _AddFilteredTypeName(s.c_str(), result); @@ -570,75 +421,76 @@ UsdMayaJobExportArgs::UsdMayaJobExportArgs( const VtDictionary& userArgs, const UsdMayaUtil::MDagPathSet& dagPaths, const std::vector& timeSamples) - : compatibility(_Token( + : compatibility(extractToken( userArgs, UsdMayaJobExportArgsTokens->compatibility, UsdMayaJobExportArgsTokens->none, { UsdMayaJobExportArgsTokens->appleArKit })) - , defaultMeshScheme(_Token( + , defaultMeshScheme(extractToken( userArgs, UsdMayaJobExportArgsTokens->defaultMeshScheme, UsdGeomTokens->catmullClark, { UsdGeomTokens->loop, UsdGeomTokens->bilinear, UsdGeomTokens->none })) - , defaultUSDFormat(_Token( + , defaultUSDFormat(extractToken( userArgs, UsdMayaJobExportArgsTokens->defaultUSDFormat, UsdUsdcFileFormatTokens->Id, { UsdUsdaFileFormatTokens->Id })) - , eulerFilter(_Boolean(userArgs, UsdMayaJobExportArgsTokens->eulerFilter)) - , excludeInvisible(_Boolean(userArgs, UsdMayaJobExportArgsTokens->renderableOnly)) + , eulerFilter(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->eulerFilter)) + , excludeInvisible(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->renderableOnly)) , exportCollectionBasedBindings( - _Boolean(userArgs, UsdMayaJobExportArgsTokens->exportCollectionBasedBindings)) - , exportColorSets(_Boolean(userArgs, UsdMayaJobExportArgsTokens->exportColorSets)) - , exportDefaultCameras(_Boolean(userArgs, UsdMayaJobExportArgsTokens->defaultCameras)) - , exportDisplayColor(_Boolean(userArgs, UsdMayaJobExportArgsTokens->exportDisplayColor)) - , exportDistanceUnit(_Boolean(userArgs, UsdMayaJobExportArgsTokens->exportDistanceUnit)) - , exportInstances(_Boolean(userArgs, UsdMayaJobExportArgsTokens->exportInstances)) + extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportCollectionBasedBindings)) + , exportColorSets(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportColorSets)) + , exportDefaultCameras(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->defaultCameras)) + , exportDisplayColor(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportDisplayColor)) + , exportDistanceUnit(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportDistanceUnit)) + , exportInstances(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportInstances)) , exportMaterialCollections( - _Boolean(userArgs, UsdMayaJobExportArgsTokens->exportMaterialCollections)) - , exportMeshUVs(_Boolean(userArgs, UsdMayaJobExportArgsTokens->exportUVs)) - , exportNurbsExplicitUV(_Boolean(userArgs, UsdMayaJobExportArgsTokens->exportUVs)) - , referenceObjectMode(_Token( + extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportMaterialCollections)) + , exportMeshUVs(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportUVs)) + , exportNurbsExplicitUV(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportUVs)) + , referenceObjectMode(extractToken( userArgs, UsdMayaJobExportArgsTokens->referenceObjectMode, UsdMayaJobExportArgsTokens->none, { UsdMayaJobExportArgsTokens->attributeOnly, UsdMayaJobExportArgsTokens->defaultToMesh })) , exportRefsAsInstanceable( - _Boolean(userArgs, UsdMayaJobExportArgsTokens->exportRefsAsInstanceable)) - , exportSkels(_Token( + extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportRefsAsInstanceable)) + , exportSkels(extractToken( userArgs, UsdMayaJobExportArgsTokens->exportSkels, UsdMayaJobExportArgsTokens->none, { UsdMayaJobExportArgsTokens->auto_, UsdMayaJobExportArgsTokens->explicit_ })) - , exportSkin(_Token( + , exportSkin(extractToken( userArgs, UsdMayaJobExportArgsTokens->exportSkin, UsdMayaJobExportArgsTokens->none, { UsdMayaJobExportArgsTokens->auto_, UsdMayaJobExportArgsTokens->explicit_ })) - , exportBlendShapes(_Boolean(userArgs, UsdMayaJobExportArgsTokens->exportBlendShapes)) - , exportVisibility(_Boolean(userArgs, UsdMayaJobExportArgsTokens->exportVisibility)) - , exportComponentTags(_Boolean(userArgs, UsdMayaJobExportArgsTokens->exportComponentTags)) - , file(_String(userArgs, UsdMayaJobExportArgsTokens->file)) - , ignoreWarnings(_Boolean(userArgs, UsdMayaJobExportArgsTokens->ignoreWarnings)) + , exportBlendShapes(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportBlendShapes)) + , exportVisibility(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportVisibility)) + , exportComponentTags(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportComponentTags)) + , file(extractString(userArgs, UsdMayaJobExportArgsTokens->file)) + , ignoreWarnings(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->ignoreWarnings)) , materialCollectionsPath( - _AbsolutePath(userArgs, UsdMayaJobExportArgsTokens->materialCollectionsPath)) - , materialsScopeName( - _GetMaterialsScopeName(_String(userArgs, UsdMayaJobExportArgsTokens->materialsScopeName))) - , mergeTransformAndShape(_Boolean(userArgs, UsdMayaJobExportArgsTokens->mergeTransformAndShape)) - , normalizeNurbs(_Boolean(userArgs, UsdMayaJobExportArgsTokens->normalizeNurbs)) - , preserveUVSetNames(_Boolean(userArgs, UsdMayaJobExportArgsTokens->preserveUVSetNames)) - , stripNamespaces(_Boolean(userArgs, UsdMayaJobExportArgsTokens->stripNamespaces)) - , parentScope(_AbsolutePath(userArgs, UsdMayaJobExportArgsTokens->parentScope)) - , renderLayerMode(_Token( + extractAbsolutePath(userArgs, UsdMayaJobExportArgsTokens->materialCollectionsPath)) + , materialsScopeName(_GetMaterialsScopeName( + extractString(userArgs, UsdMayaJobExportArgsTokens->materialsScopeName))) + , mergeTransformAndShape( + extractBoolean(userArgs, UsdMayaJobExportArgsTokens->mergeTransformAndShape)) + , normalizeNurbs(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->normalizeNurbs)) + , preserveUVSetNames(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->preserveUVSetNames)) + , stripNamespaces(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->stripNamespaces)) + , parentScope(extractAbsolutePath(userArgs, UsdMayaJobExportArgsTokens->parentScope)) + , renderLayerMode(extractToken( userArgs, UsdMayaJobExportArgsTokens->renderLayerMode, UsdMayaJobExportArgsTokens->defaultLayer, { UsdMayaJobExportArgsTokens->currentLayer, UsdMayaJobExportArgsTokens->modelingVariant })) - , rootKind(_String(userArgs, UsdMayaJobExportArgsTokens->kind)) + , rootKind(extractString(userArgs, UsdMayaJobExportArgsTokens->kind)) , disableModelKindProcessor( - _Boolean(userArgs, UsdMayaJobExportArgsTokens->disableModelKindProcessor)) - , shadingMode(_Token( + extractBoolean(userArgs, UsdMayaJobExportArgsTokens->disableModelKindProcessor)) + , shadingMode(extractToken( userArgs, UsdMayaJobExportArgsTokens->shadingMode, UsdMayaShadingModeTokens->useRegistry, @@ -647,23 +499,25 @@ UsdMayaJobExportArgs::UsdMayaJobExportArgs( exporters.emplace_back(UsdMayaShadingModeTokens->none); return exporters; }())) - , allMaterialConversions(_TokenSet(userArgs, UsdMayaJobExportArgsTokens->convertMaterialsTo)) - , verbose(_Boolean(userArgs, UsdMayaJobExportArgsTokens->verbose)) - , staticSingleSample(_Boolean(userArgs, UsdMayaJobExportArgsTokens->staticSingleSample)) - , geomSidedness(_Token( + , allMaterialConversions( + extractTokenSet(userArgs, UsdMayaJobExportArgsTokens->convertMaterialsTo)) + , verbose(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->verbose)) + , staticSingleSample(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->staticSingleSample)) + , geomSidedness(extractToken( userArgs, UsdMayaJobExportArgsTokens->geomSidedness, UsdMayaJobExportArgsTokens->derived, { UsdMayaJobExportArgsTokens->single, UsdMayaJobExportArgsTokens->double_ })) - , includeAPINames(_TokenSet(userArgs, UsdMayaJobExportArgsTokens->apiSchema)) - , jobContextNames(_TokenSet(userArgs, UsdMayaJobExportArgsTokens->jobContext)) - , chaserNames(_Vector(userArgs, UsdMayaJobExportArgsTokens->chaser)) + , includeAPINames(extractTokenSet(userArgs, UsdMayaJobExportArgsTokens->apiSchema)) + , jobContextNames(extractTokenSet(userArgs, UsdMayaJobExportArgsTokens->jobContext)) + , chaserNames(extractVector(userArgs, UsdMayaJobExportArgsTokens->chaser)) , allChaserArgs(_ChaserArgs(userArgs, UsdMayaJobExportArgsTokens->chaserArgs)) , remapUVSetsTo(_UVSetRemaps(userArgs, UsdMayaJobExportArgsTokens->remapUVSetsTo)) - , melPerFrameCallback(_String(userArgs, UsdMayaJobExportArgsTokens->melPerFrameCallback)) - , melPostCallback(_String(userArgs, UsdMayaJobExportArgsTokens->melPostCallback)) - , pythonPerFrameCallback(_String(userArgs, UsdMayaJobExportArgsTokens->pythonPerFrameCallback)) - , pythonPostCallback(_String(userArgs, UsdMayaJobExportArgsTokens->pythonPostCallback)) + , melPerFrameCallback(extractString(userArgs, UsdMayaJobExportArgsTokens->melPerFrameCallback)) + , melPostCallback(extractString(userArgs, UsdMayaJobExportArgsTokens->melPostCallback)) + , pythonPerFrameCallback( + extractString(userArgs, UsdMayaJobExportArgsTokens->pythonPerFrameCallback)) + , pythonPostCallback(extractString(userArgs, UsdMayaJobExportArgsTokens->pythonPostCallback)) , dagPaths(dagPaths) , timeSamples(timeSamples) , rootMapFunction(PcpMapFunction::Create( @@ -898,12 +752,13 @@ void UsdMayaJobExportArgs::GetDictionaryTimeSamples( const VtDictionary& userArgs, std::vector& timeSamples) { - const bool exportAnimation = _Boolean(userArgs, UsdMayaJobExportArgsTokens->animation); - const double startTime = _Double(userArgs, UsdMayaJobExportArgsTokens->startTime, 1.0); - const double endTime = _Double(userArgs, UsdMayaJobExportArgsTokens->endTime, 1.0); - const double frameStride = _Double(userArgs, UsdMayaJobExportArgsTokens->frameStride, 1.0); + const bool exportAnimation = extractBoolean(userArgs, UsdMayaJobExportArgsTokens->animation); + const double startTime = extractDouble(userArgs, UsdMayaJobExportArgsTokens->startTime, 1.0); + const double endTime = extractDouble(userArgs, UsdMayaJobExportArgsTokens->endTime, 1.0); + const double frameStride + = extractDouble(userArgs, UsdMayaJobExportArgsTokens->frameStride, 1.0); const std::vector samples - = _Vector(userArgs, UsdMayaJobExportArgsTokens->frameSample); + = extractVector(userArgs, UsdMayaJobExportArgsTokens->frameSample); std::set frameSamples(samples.begin(), samples.end()); @@ -1100,32 +955,32 @@ UsdMayaJobImportArgs::UsdMayaJobImportArgs( const VtDictionary& userArgs, const bool importWithProxyShapes, const GfInterval& timeInterval) - : assemblyRep(_Token( + : assemblyRep(extractToken( userArgs, UsdMayaJobImportArgsTokens->assemblyRep, UsdMayaJobImportArgsTokens->Collapsed, { UsdMayaJobImportArgsTokens->Full, UsdMayaJobImportArgsTokens->Import, UsdMayaJobImportArgsTokens->Unloaded })) - , excludePrimvarNames(_TokenSet(userArgs, UsdMayaJobImportArgsTokens->excludePrimvar)) - , includeAPINames(_TokenSet(userArgs, UsdMayaJobImportArgsTokens->apiSchema)) - , jobContextNames(_TokenSet(userArgs, UsdMayaJobImportArgsTokens->jobContext)) - , includeMetadataKeys(_TokenSet(userArgs, UsdMayaJobImportArgsTokens->metadata)) + , excludePrimvarNames(extractTokenSet(userArgs, UsdMayaJobImportArgsTokens->excludePrimvar)) + , includeAPINames(extractTokenSet(userArgs, UsdMayaJobImportArgsTokens->apiSchema)) + , jobContextNames(extractTokenSet(userArgs, UsdMayaJobImportArgsTokens->jobContext)) + , includeMetadataKeys(extractTokenSet(userArgs, UsdMayaJobImportArgsTokens->metadata)) , shadingModes(_shadingModesImportArgs(userArgs, UsdMayaJobImportArgsTokens->shadingMode)) - , preferredMaterial(_Token( + , preferredMaterial(extractToken( userArgs, UsdMayaJobImportArgsTokens->preferredMaterial, UsdMayaPreferredMaterialTokens->none, UsdMayaPreferredMaterialTokens->allTokens)) , importUSDZTexturesFilePath(UsdMayaJobImportArgs::GetImportUSDZTexturesFilePath(userArgs)) - , importUSDZTextures(_Boolean(userArgs, UsdMayaJobImportArgsTokens->importUSDZTextures)) - , importInstances(_Boolean(userArgs, UsdMayaJobImportArgsTokens->importInstances)) - , useAsAnimationCache(_Boolean(userArgs, UsdMayaJobImportArgsTokens->useAsAnimationCache)) + , importUSDZTextures(extractBoolean(userArgs, UsdMayaJobImportArgsTokens->importUSDZTextures)) + , importInstances(extractBoolean(userArgs, UsdMayaJobImportArgsTokens->importInstances)) + , useAsAnimationCache(extractBoolean(userArgs, UsdMayaJobImportArgsTokens->useAsAnimationCache)) , importWithProxyShapes(importWithProxyShapes) - , preserveTimeline(_Boolean(userArgs, UsdMayaJobImportArgsTokens->preserveTimeline)) - , pullImportStage(_UsdStageRefPtr(userArgs, UsdMayaJobImportArgsTokens->pullImportStage)) + , preserveTimeline(extractBoolean(userArgs, UsdMayaJobImportArgsTokens->preserveTimeline)) + , pullImportStage(extractUsdStageRefPtr(userArgs, UsdMayaJobImportArgsTokens->pullImportStage)) , timeInterval(timeInterval) - , chaserNames(_Vector(userArgs, UsdMayaJobImportArgsTokens->chaser)) + , chaserNames(extractVector(userArgs, UsdMayaJobImportArgsTokens->chaser)) , allChaserArgs(_ChaserArgs(userArgs, UsdMayaJobImportArgsTokens->chaserArgs)) { } @@ -1265,11 +1120,11 @@ const VtDictionary& UsdMayaJobImportArgs::GetGuideDictionary() const std::string UsdMayaJobImportArgs::GetImportUSDZTexturesFilePath(const VtDictionary& userArgs) { - if (!_Boolean(userArgs, UsdMayaJobImportArgsTokens->importUSDZTextures)) + if (!extractBoolean(userArgs, UsdMayaJobImportArgsTokens->importUSDZTextures)) return ""; // Not importing textures. File path stays empty. const std::string pathArg - = _String(userArgs, UsdMayaJobImportArgsTokens->importUSDZTexturesFilePath); + = extractString(userArgs, UsdMayaJobImportArgsTokens->importUSDZTexturesFilePath); std::string importTexturesRootDirPath; if (pathArg.size() == 0) { // NOTE: (yliangsiew) If the user gives an empty argument, we'll try // to determine the best directory to write to instead. diff --git a/lib/mayaUsd/utils/CMakeLists.txt b/lib/mayaUsd/utils/CMakeLists.txt index 0162aa33e7..20f56e43b7 100644 --- a/lib/mayaUsd/utils/CMakeLists.txt +++ b/lib/mayaUsd/utils/CMakeLists.txt @@ -21,6 +21,7 @@ target_sources(${PROJECT_NAME} traverseLayer.cpp undoHelperCommand.cpp util.cpp + utilDictionary.cpp utilFileSystem.cpp utilSerialization.cpp variants.cpp @@ -44,6 +45,7 @@ set(HEADERS traverseLayer.h undoHelperCommand.h util.h + utilDictionary.h utilFileSystem.h utilSerialization.h variants.h diff --git a/lib/mayaUsd/utils/utilDictionary.cpp b/lib/mayaUsd/utils/utilDictionary.cpp new file mode 100644 index 0000000000..bf6a1ec551 --- /dev/null +++ b/lib/mayaUsd/utils/utilDictionary.cpp @@ -0,0 +1,143 @@ +// +// Copyright 2021 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. +// +#include "utilDictionary.h" + +namespace MAYAUSD_NS_DEF { + +namespace DictUtils { + +using namespace PXR_NS; + +/// Extracts a bool at \p key from \p userArgs, or false if it can't extract. +bool extractBoolean(const VtDictionary& userArgs, const TfToken& key) +{ + if (!VtDictionaryIsHolding(userArgs, key)) { + TF_CODING_ERROR( + "Dictionary is missing required key '%s' or key is " + "not bool type", + key.GetText()); + return false; + } + return VtDictionaryGet(userArgs, key); +} + +/// Extracts a pointer at \p key from \p userArgs, or nullptr if it can't extract. +UsdStageRefPtr extractUsdStageRefPtr(const VtDictionary& userArgs, const TfToken& key) +{ + if (!VtDictionaryIsHolding(userArgs, key)) { + TF_CODING_ERROR( + "Dictionary is missing required key '%s' or key is " + "not pointer type", + key.GetText()); + return nullptr; + } + return VtDictionaryGet(userArgs, key); +} + +/// Extracts a double at \p key from \p userArgs, or defaultValue if it can't extract. +double extractDouble(const VtDictionary& userArgs, const TfToken& key, double defaultValue) +{ + if (VtDictionaryIsHolding(userArgs, key)) + return VtDictionaryGet(userArgs, key); + + // Since user dictionary can be provided from Python and in Python it is easy to + // mix int and double, especially since value literal will take the simplest value + // they can, for example 0 will be an int, support receiving the value as an integer. + if (VtDictionaryIsHolding(userArgs, key)) + return VtDictionaryGet(userArgs, key); + + TF_CODING_ERROR( + "Dictionary is missing required key '%s' or key is " + "not double type", + key.GetText()); + return defaultValue; +} + +/// Extracts a string at \p key from \p userArgs, or "" if it can't extract. +std::string extractString(const VtDictionary& userArgs, const TfToken& key) +{ + if (!VtDictionaryIsHolding(userArgs, key)) { + TF_CODING_ERROR( + "Dictionary is missing required key '%s' or key is " + "not string type", + key.GetText()); + return std::string(); + } + return VtDictionaryGet(userArgs, key); +} + +/// Extracts a token at \p key from \p userArgs. +/// If the token value is not either \p defaultToken or one of the +/// \p otherTokens, then returns \p defaultToken instead. +TfToken extractToken( + const VtDictionary& userArgs, + const TfToken& key, + const TfToken& defaultToken, + const std::vector& otherTokens) +{ + const TfToken tok(extractString(userArgs, key)); + for (const TfToken& allowedTok : otherTokens) { + if (tok == allowedTok) { + return tok; + } + } + + // Empty token will silently be promoted to default value. + // Warning for non-empty tokens that don't match. + if (tok != defaultToken && !tok.IsEmpty()) { + TF_WARN( + "Value '%s' is not allowed for flag '%s'; using fallback '%s' " + "instead", + tok.GetText(), + key.GetText(), + defaultToken.GetText()); + } + return defaultToken; +} + +/// Extracts an absolute path at \p key from \p userArgs, or the empty path if +/// it can't extract. +SdfPath extractAbsolutePath(const VtDictionary& userArgs, const TfToken& key) +{ + const std::string s = extractString(userArgs, key); + // Assume that empty strings are empty paths. (This might be an error case.) + if (s.empty()) { + return SdfPath(); + } + // Make all relative paths into absolute paths. + SdfPath path(s); + if (path.IsAbsolutePath()) { + return path; + } else { + return SdfPath::AbsoluteRootPath().AppendPath(path); + } +} + +/// Convenience function that takes the result of extractVector and converts it to a +/// TfToken::Set. +TfToken::Set extractTokenSet(const VtDictionary& userArgs, const TfToken& key) +{ + const std::vector vec = extractVector(userArgs, key); + TfToken::Set result; + for (const std::string& s : vec) { + result.insert(TfToken(s)); + } + return result; +} + +} // namespace DictUtils + +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/utils/utilDictionary.h b/lib/mayaUsd/utils/utilDictionary.h new file mode 100644 index 0000000000..4e35951ef5 --- /dev/null +++ b/lib/mayaUsd/utils/utilDictionary.h @@ -0,0 +1,128 @@ +// +// Copyright 2021 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. +// +#ifndef MAYA_USD_UTIL_DICTIONARY_H +#define MAYA_USD_UTIL_DICTIONARY_H + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace MAYAUSD_NS_DEF { + +namespace DictUtils { + +/// \brief Extracts a bool at \p key from \p userArgs, or false if it can't extract. +MAYAUSD_CORE_PUBLIC +bool extractBoolean(const PXR_NS::VtDictionary& userArgs, const PXR_NS::TfToken& key); + +/// \brief Extracts a pointer at \p key from \p userArgs, or nullptr if it can't extract. +MAYAUSD_CORE_PUBLIC +PXR_NS::UsdStageRefPtr +extractUsdStageRefPtr(const PXR_NS::VtDictionary& userArgs, const PXR_NS::TfToken& key); + +/// \brief Extracts a double at \p key from \p userArgs, or defaultValue if it can't extract. +MAYAUSD_CORE_PUBLIC +double extractDouble( + const PXR_NS::VtDictionary& userArgs, + const PXR_NS::TfToken& key, + double defaultValue); + +/// \brief Extracts a string at \p key from \p userArgs, or "" if it can't extract. +MAYAUSD_CORE_PUBLIC +std::string extractString(const PXR_NS::VtDictionary& userArgs, const PXR_NS::TfToken& key); + +/// \brief Extracts a token at \p key from \p userArgs. +/// If the token value is not either \p defaultToken or one of the +/// \p otherTokens, then returns \p defaultToken instead. +MAYAUSD_CORE_PUBLIC +PXR_NS::TfToken extractToken( + const PXR_NS::VtDictionary& userArgs, + const PXR_NS::TfToken& key, + const PXR_NS::TfToken& defaultToken, + const std::vector& otherTokens); + +/// \brief Extracts an absolute path at \p key from \p userArgs, or the empty path if +/// it can't extract. +MAYAUSD_CORE_PUBLIC +PXR_NS::SdfPath +extractAbsolutePath(const PXR_NS::VtDictionary& userArgs, const PXR_NS::TfToken& key); + +/// \brief Extracts an vector from the vector at \p key in \p userArgs. +/// Returns an empty vector if it can't convert the entire value at \p key into +/// a vector. +template +MAYAUSD_CORE_PUBLIC std::vector + extractVector(const PXR_NS::VtDictionary& userArgs, const PXR_NS::TfToken& key); + +/// \brief Convenience function that takes the result of extractVector and converts it to a +/// TfToken::Set. +MAYAUSD_CORE_PUBLIC +PXR_NS::TfToken::Set +extractTokenSet(const PXR_NS::VtDictionary& userArgs, const PXR_NS::TfToken& key); + +// Implementation of the templated function declared above. +template +MAYAUSD_CORE_PUBLIC std::vector + extractVector(const PXR_NS::VtDictionary& userArgs, const PXR_NS::TfToken& key) +{ + // Using declaration is necessary for the TF_ macros to compile as they assume + // to be in that namespace. + using namespace PXR_NS; + + // Check that vector exists. + if (VtDictionaryIsHolding>(userArgs, key)) { + std::vector vals = VtDictionaryGet>(userArgs, key); + return vals; + } + + if (!VtDictionaryIsHolding>(userArgs, key)) { + TF_CODING_ERROR( + "Dictionary is missing required key '%s' or key is " + "not vector type", + key.GetText()); + return std::vector(); + } + + // Check that vector is correctly-typed. + std::vector vals = VtDictionaryGet>(userArgs, key); + if (!std::all_of(vals.begin(), vals.end(), [](const VtValue& v) { return v.IsHolding(); })) { + TF_CODING_ERROR( + "Vector at dictionary key '%s' contains elements of " + "the wrong type", + key.GetText()); + return std::vector(); + } + + // Extract values. + std::vector result; + for (const VtValue& v : vals) { + result.push_back(v.UncheckedGet()); + } + return result; +} + +} // namespace DictUtils + +} // namespace MAYAUSD_NS_DEF + +#endif // MAYA_USD_UTIL_DICTIONARY_H diff --git a/test/lib/usd/translators/testUsdExportRoots.py b/test/lib/usd/translators/testUsdExportRoots.py index 95e4050f57..4377e53ba2 100644 --- a/test/lib/usd/translators/testUsdExportRoots.py +++ b/test/lib/usd/translators/testUsdExportRoots.py @@ -33,14 +33,15 @@ class testUsdExportRoot(unittest.TestCase): @classmethod def setUpClass(cls): inputPath = fixturesUtils.setUpClass(__file__) - - filePath = os.path.join(inputPath, "UsdExportRootsTest", "UsdExportRootsTest.ma") - cmds.file(filePath, force=True, open=True) + cls.filePath = os.path.join(inputPath, "UsdExportRootsTest", "UsdExportRootsTest.ma") @classmethod def tearDownClass(cls): standalone.uninitialize() + def setUp(self): + cmds.file(testUsdExportRoot.filePath, force=True, open=True) + def doExportImportOneMethod(self, method, shouldError=False, root=None, selection=None): if isinstance(root, str): root = [root] @@ -100,11 +101,11 @@ def doExportImportTest(self, validator, shouldError=False, root=None, selection= def assertPrim(self, stage, path, type): prim = stage.GetPrimAtPath(path) - self.assertTrue(prim.IsValid()) - self.assertEqual(prim.GetTypeName(), type) + self.assertTrue(prim.IsValid(), "Expected to find %s" % path) + self.assertEqual(prim.GetTypeName(), type, "Expected prim %s to have type %s" % (path, type)) def assertNotPrim(self, stage, path): - self.assertFalse(stage.GetPrimAtPath(path).IsValid()) + self.assertFalse(stage.GetPrimAtPath(path).IsValid(), "Did not expect to find %s" % path) def testNonOverlappingSelectionRoot(self): # test that the command errors if we give it a root + selection that @@ -306,5 +307,28 @@ def validator(stage): pass self.doExportImportTest(validator, shouldError=True, root='NoneExisting', selection='OtherTop') + def testExportRootStripNamespace(self): + # Test that stripnamespace works with export roots when stripped node names would conflict + # but only one was specified as root. + + cmds.file(new=True, force=True) + + cmds.namespace(add="myAssetA") + cmds.polySphere(name="myAssetA:sphere_GEO") + cmds.namespace(add="myAssetB") + cmds.polySphere(name="myAssetB:sphere_GEO") + + usdFile = os.path.abspath("testExportRootStripNamespace.usda") + + cmds.mayaUSDExport(file=usdFile, + exportRoots=['|myAssetA:sphere_GEO'], + stripNamespaces=True, + ) + + stage = Usd.Stage.Open(usdFile) + + self.assertPrim(stage, '/sphere_GEO', 'Mesh') + + if __name__ == '__main__': unittest.main(verbosity=2)