diff --git a/lib/mayaUsd/utils/CMakeLists.txt b/lib/mayaUsd/utils/CMakeLists.txt index 974853e004..7785d69dcd 100644 --- a/lib/mayaUsd/utils/CMakeLists.txt +++ b/lib/mayaUsd/utils/CMakeLists.txt @@ -22,6 +22,7 @@ target_sources(${PROJECT_NAME} util.cpp utilFileSystem.cpp utilSerialization.cpp + variants.cpp ) set(HEADERS @@ -43,6 +44,7 @@ set(HEADERS util.h utilFileSystem.h utilSerialization.h + variants.h ) set(PLUGINFO diff --git a/lib/mayaUsd/utils/variants.cpp b/lib/mayaUsd/utils/variants.cpp new file mode 100644 index 0000000000..7b78fe2d2f --- /dev/null +++ b/lib/mayaUsd/utils/variants.cpp @@ -0,0 +1,62 @@ +// +// 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. +// +#include "variants.h" + +#include + +/// General utility functions for variants +namespace MAYAUSD_NS_DEF { + +void applyToAllVariants( + const PXR_NS::UsdPrim& primWithVariants, + bool includeNonVariant, + const std::function& func) +{ + // Record if we saw at least one variant to apply the function on. + // Used when non-variant are included. + bool atLeastOneVariant = false; + + // Clear the edit flag in all other variants, in all variant sets if any. + PXR_NS::UsdStagePtr stage = primWithVariants.GetStage(); + PXR_NS::UsdVariantSets variantSets = primWithVariants.GetVariantSets(); + for (const std::string& variantSetName : variantSets.GetNames()) { + + PXR_NS::UsdVariantSet variantSet = primWithVariants.GetVariantSet(variantSetName); + + // Make sure to restore the current selected variant even if the face + // of exceptions. + AutoVariantRestore variantRestore(variantSet); + + for (const std::string& variantName : variantSet.GetVariantNames()) { + if (variantSet.SetVariantSelection(variantName)) { + PXR_NS::UsdEditTarget target = stage->GetEditTarget(); + + PXR_NS::UsdEditContext switchEditContext( + stage, variantSet.GetVariantEditTarget(target.GetLayer())); + + func(); + atLeastOneVariant = true; + } + } + } + + // When not a single variant was found and the caller wants to apply + // the function even in the absence of vairants, call it now. + if (includeNonVariant && !atLeastOneVariant) + func(); +} + +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/utils/variants.h b/lib/mayaUsd/utils/variants.h new file mode 100644 index 0000000000..603e4f2297 --- /dev/null +++ b/lib/mayaUsd/utils/variants.h @@ -0,0 +1,75 @@ +// +// 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. +// +#ifndef MAYAUSD_UTILS_VARIANTS_H +#define MAYAUSD_UTILS_VARIANTS_H + +#include + +#include +#include +#include + +#include +#include + +/// General utility functions for variants +namespace MAYAUSD_NS_DEF { + +/*! \brief Apply a function to all variants on a prim. + + Optionally, if includeNonVariant is true, apply it even if the prim + has no variant at all, which is useful when you want to edit something + on all variations of a prim, even if there are no variations. +*/ +MAYAUSD_CORE_PUBLIC +void applyToAllVariants( + const PXR_NS::UsdPrim& primWithVariants, + bool includeNonVariant, + const std::function& func); + +/*! \brief Keep track of the current variant and restore it on destruction. + + The reason we don't make this an variant auto-switcher is that + switching variant recomposes the stage and one main user of the + restore is visiting all variants, which would double the number + of recompose if we restored the variant between each visit. + + IOW, for a set with three variants A, B, C, this design permits + the switch Current -> A -> B -> C -> Current instead of doing + Current -> A -> Current -> B -> Current -> C -> Current. +*/ + +class AutoVariantRestore +{ +public: + MAYAUSD_CORE_PUBLIC + AutoVariantRestore(PXR_NS::UsdVariantSet& variantSet) + : _variantSet(variantSet) + , _variant(variantSet.GetVariantSelection()) + { + } + + MAYAUSD_CORE_PUBLIC + ~AutoVariantRestore() { _variantSet.SetVariantSelection(_variant); } + +private: + PXR_NS::UsdVariantSet& _variantSet; + std::string _variant; +}; + +} // namespace MAYAUSD_NS_DEF + +#endif // MAYAUSD_UTILS_VARIANTS_H diff --git a/lib/usd/translators/mayaReferenceUpdater.cpp b/lib/usd/translators/mayaReferenceUpdater.cpp index ef2f24de43..8034b267ea 100644 --- a/lib/usd/translators/mayaReferenceUpdater.cpp +++ b/lib/usd/translators/mayaReferenceUpdater.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -48,14 +49,21 @@ namespace { +// Clear the auto-edit flag on a USD Maya Reference so that it does not +// get edited immediately again. Clear in all variants, since each +// variant has its own copy of the flag. void clearAutoEdit(const UsdPrim& prim) { - if (prim.IsValid()) { + UsdPrim parentPrim = prim.GetParent(); + MAYAUSD_NS::applyToAllVariants(parentPrim, true, [prim]() { + // Note: the prim might not exist in all variants, so check its validity. + if (!prim.IsValid()) + return; + UsdAttribute mayaAutoEditAttr = prim.GetAttribute(MayaUsd_SchemasTokens->mayaAutoEdit); - if (mayaAutoEditAttr.IsValid()) { + if (mayaAutoEditAttr.IsValid()) mayaAutoEditAttr.Set(false); - } - } + }); } std::string findValue(const PXR_NS::VtDictionary& routingData, const PXR_NS::TfToken& key) @@ -214,9 +222,6 @@ UsdMayaPrimUpdater::PushCopySpecs PxrUsdTranslators_MayaReferenceUpdater::pushCo // The Maya reference is meant as a cache, and therefore fully // overwritten, so we don't call MayaUsdUtils::mergePrims(). - // As of 13-Dec-2021 pushEnd() will not be called on the - // MayaReferenceUpdater, because the prim updater type information - // is not correctly preserved. Unload the reference here. PPT. if (SdfCopySpec(srcLayer, srcSdfPath, dstLayer, dstPath)) { const MObject& parentNode = getMayaObject(); UsdMayaTranslatorMayaReference::UnloadMayaReference(parentNode); @@ -256,11 +261,6 @@ bool PxrUsdTranslators_MayaReferenceUpdater::discardEdits() /* virtual */ bool PxrUsdTranslators_MayaReferenceUpdater::pushEnd() { - // As of 25-Feb-2022 the Maya transform node pulled from the Maya reference - // prim ends up being unlocked by the unlock traversal in - // PrimUpdaterManager::mergeToUsd(). However, more robust to enforce - // separation of concerns and perform the inverse of editAsMaya() here. - // Unnecessary to unlock individual attributes, as the Maya transform node // is removed at pushEnd(). MDagPath transformPath;