From 4842fdbd80cb1973861bb4c90f881e82d3ae7ffd Mon Sep 17 00:00:00 2001 From: Pierre Baillargeon Date: Tue, 15 Oct 2024 15:17:32 -0400 Subject: [PATCH] EMSUSD-1722 find the correct layer manager node When loading a Maya scene containing a stage, possibly through other Maya reference, find the correct layer manager node for the stage. Each layer manager node is specific to the scene or reference. - Add an optional parameter to some layer manager function to specify the Maya reference that should contain the information. - Pass the correct Maya reference, if any, when computing the proxy shape node layers. - Add a unit test to load a stage in a Maya reference. --- lib/mayaUsd/nodes/layerManager.cpp | 52 ++++++---- lib/mayaUsd/nodes/layerManager.h | 6 +- lib/mayaUsd/nodes/proxyShapeBase.cpp | 37 ++++++- test/lib/CMakeLists.txt | 1 + test/lib/testMayaUsdCreateStageInMayaRef.py | 103 ++++++++++++++++++++ 5 files changed, 174 insertions(+), 25 deletions(-) create mode 100644 test/lib/testMayaUsdCreateStageInMayaRef.py diff --git a/lib/mayaUsd/nodes/layerManager.cpp b/lib/mayaUsd/nodes/layerManager.cpp index 63b239a0b2..ce3c594ceb 100644 --- a/lib/mayaUsd/nodes/layerManager.cpp +++ b/lib/mayaUsd/nodes/layerManager.cpp @@ -114,7 +114,16 @@ MStatus disconnectCompoundArrayPlug(MPlug arrayPlug) return dgmod.doIt(); } -MayaUsd::LayerManager* findNode() +static bool isFromReference(const MFnReference* fromReference, const MFnDependencyNode& node) +{ + if (fromReference) { + return fromReference->containsNodeExactly(node.object()); + } else { + return !node.isFromReferencedFile(); + } +} + +MayaUsd::LayerManager* findNode(const MFnReference* fromReference) { // Check for cached layer manager before searching MFnDependencyNode fn; @@ -130,7 +139,7 @@ MayaUsd::LayerManager* findNode() for (; !iter.isDone(); iter.next()) { MObject mobj = iter.item(); fn.setObject(mobj); - if (fn.typeId() == MayaUsd::LayerManager::typeId && !fn.isFromReferencedFile()) { + if (fn.typeId() == MayaUsd::LayerManager::typeId && isFromReference(fromReference, fn)) { layerManagerHandle = mobj; return static_cast(fn.userNode()); } @@ -138,9 +147,9 @@ MayaUsd::LayerManager* findNode() return nullptr; } -MayaUsd::LayerManager* findOrCreateNode() +MayaUsd::LayerManager* findOrCreateNode(const MFnReference* fromReference) { - MayaUsd::LayerManager* lm = findNode(); + MayaUsd::LayerManager* lm = findNode(fromReference); if (!lm) { MDGModifier& modifier = MayaUsd::MDGModifierUndoItem::create("Node find or creation"); MObject manager = modifier.createNode(MayaUsd::LayerManager::typeId); @@ -217,7 +226,7 @@ class LayerDatabase : public TfWeakBase static void cleanupForExport(void*); static void prepareForWriteCheck(bool*, bool); static void cleanupForWrite(); - static void loadLayersPostRead(void*); + static void loadLayersPostRead(const MFnReference* fromReference); static void cleanUpNewScene(void*); static void clearManagerNode(MayaUsd::LayerManager* lm); static void removeManagerNode(MayaUsd::LayerManager* lm = nullptr); @@ -600,7 +609,8 @@ std::string LayerDatabase::getSelectedStage() const { return _selectedStage; } bool LayerDatabase::saveLayerManagerSelectedStage() { - MayaUsd::LayerManager* lm = findOrCreateNode(); + const MFnReference* fromReference = nullptr; + MayaUsd::LayerManager* lm = findOrCreateNode(fromReference); if (!lm) return false; @@ -627,7 +637,8 @@ bool LayerDatabase::saveLayerManagerSelectedStage() bool LayerDatabase::loadLayerManagerSelectedStage() { - MayaUsd::LayerManager* lm = findNode(); + const MFnReference* fromReference = nullptr; + MayaUsd::LayerManager* lm = findNode(fromReference); if (!lm) return false; @@ -845,7 +856,8 @@ SaveStageToMayaResult saveStageToMayaFile( SaveStageToMayaResult saveStageToMayaFile(const MObject& proxyNode, UsdStageRefPtr stage) { SaveStageToMayaResult result; - MayaUsd::LayerManager* lm = findOrCreateNode(); + const MFnReference* fromReference = nullptr; + MayaUsd::LayerManager* lm = findOrCreateNode(fromReference); if (!lm) return result; @@ -866,7 +878,8 @@ SaveStageToMayaResult saveStageToMayaFile(const MObject& proxyNode, UsdStageRefP BatchSaveResult LayerDatabase::saveUsdToMayaFile() { - MayaUsd::LayerManager* lm = findOrCreateNode(); + const MFnReference* fromReference = nullptr; + MayaUsd::LayerManager* lm = findOrCreateNode(fromReference); if (!lm) { return MayaUsd::kNotHandled; } @@ -1009,7 +1022,8 @@ void LayerDatabase::convertAnonymousLayers( void LayerDatabase::saveUsdLayerToMayaFile(SdfLayerRefPtr layer, bool asAnonymous) { - MayaUsd::LayerManager* lm = findOrCreateNode(); + const MFnReference* fromReference = nullptr; + MayaUsd::LayerManager* lm = findOrCreateNode(fromReference); if (!lm) return; @@ -1026,9 +1040,9 @@ void LayerDatabase::saveUsdLayerToMayaFile(SdfLayerRefPtr layer, bool asAnonymou dataBlock.setClean(lm->layers); } -void LayerDatabase::loadLayersPostRead(void*) +void LayerDatabase::loadLayersPostRead(const MFnReference* fromReference) { - MayaUsd::LayerManager* lm = findNode(); + MayaUsd::LayerManager* lm = findNode(fromReference); if (!lm) return; @@ -1267,7 +1281,8 @@ void LayerDatabase::clearManagerNode(MayaUsd::LayerManager* lm) void LayerDatabase::removeManagerNode(MayaUsd::LayerManager* lm) { if (!lm) { - lm = findNode(); + const MFnReference* fromReference = nullptr; + lm = findNode(fromReference); } if (!lm) { return; @@ -1417,21 +1432,21 @@ LayerManager::LayerManager() LayerManager::~LayerManager() { } /* static */ -SdfLayerHandle LayerManager::findLayer(std::string identifier) +SdfLayerHandle LayerManager::findLayer(std::string identifier, const MFnReference* fromReference) { std::lock_guard lock(findNodeMutex); - LayerDatabase::loadLayersPostRead(nullptr); + LayerDatabase::loadLayersPostRead(fromReference); return LayerDatabase::instance().findLayer(identifier); } /* static */ -LayerManager::LayerNameMap LayerManager::getLayerNameMap() +LayerManager::LayerNameMap LayerManager::getLayerNameMap(const MFnReference* fromReference) { std::lock_guard lock(findNodeMutex); - LayerDatabase::loadLayersPostRead(nullptr); + LayerDatabase::loadLayersPostRead(fromReference); return LayerDatabase::instance().getLayerNameMap(); } @@ -1462,7 +1477,8 @@ void LayerManager::setSelectedStage(const std::string& stage) /* static */ std::string LayerManager::getSelectedStage() { - LayerDatabase::loadLayersPostRead(nullptr); + const MFnReference* fromReference = nullptr; + LayerDatabase::loadLayersPostRead(fromReference); return LayerDatabase::instance().getSelectedStage(); } diff --git a/lib/mayaUsd/nodes/layerManager.h b/lib/mayaUsd/nodes/layerManager.h index 19e3a58717..9f5e5bf947 100644 --- a/lib/mayaUsd/nodes/layerManager.h +++ b/lib/mayaUsd/nodes/layerManager.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -128,10 +129,11 @@ class MAYAUSD_CORE_PUBLIC LayerManager : public MPxNode recreated layer, and all sublayers, with edits from a previous Maya session and should be used to initialize the Proxy Shape in a call to UsdStage::Open(). */ - static SdfLayerHandle findLayer(std::string identifier); + static SdfLayerHandle + findLayer(std::string identifier, const MFnReference* fromReference = nullptr); using LayerNameMap = std::map; - static LayerNameMap getLayerNameMap(); + static LayerNameMap getLayerNameMap(const MFnReference* fromReference = nullptr); //! \brief returns true if the layer manager is currently saving files. static bool isSaving(); diff --git a/lib/mayaUsd/nodes/proxyShapeBase.cpp b/lib/mayaUsd/nodes/proxyShapeBase.cpp index 3da91ef3f4..9b8ddd9265 100644 --- a/lib/mayaUsd/nodes/proxyShapeBase.cpp +++ b/lib/mayaUsd/nodes/proxyShapeBase.cpp @@ -684,12 +684,35 @@ MStatus MayaUsdProxyShapeBase::compute(const MPlug& plug, MDataBlock& dataBlock) return MS::kUnknownParameter; } +static std::unique_ptr getMayaReferenceOrigin(const MayaUsdProxyShapeBase& proxyShape) +{ + MObject proxyShapeNode(proxyShape.thisMObject()); + + MStringArray referenceFileNames; + MFileIO::getReferences(referenceFileNames); + for (unsigned int rfni = 0; rfni < referenceFileNames.length(); ++rfni) { + MSelectionList selectionList; + selectionList.add(referenceFileNames[rfni]); + MObject referenceObject; + selectionList.getDependNode(0, referenceObject); + MFnReference mayaRef(referenceObject); + + if (mayaRef.containsNode(proxyShapeNode)) { + return std::make_unique(referenceObject); + } + } + + return {}; +} + /* virtual */ SdfLayerRefPtr MayaUsdProxyShapeBase::computeRootLayer(MDataBlock& dataBlock, const std::string&) { if (LayerManager::supportedNodeType(MPxNode::typeId())) { - auto rootLayerName = dataBlock.inputValue(rootLayerNameAttr).asString(); - return LayerManager::findLayer(UsdMayaUtil::convert(rootLayerName)); + auto rootLayerName = dataBlock.inputValue(rootLayerNameAttr).asString(); + auto mayaRef = getMayaReferenceOrigin(*this); + const MFnReference* fromReference = mayaRef ? mayaRef.get() : nullptr; + return LayerManager::findLayer(UsdMayaUtil::convert(rootLayerName), fromReference); } else { return nullptr; } @@ -700,7 +723,9 @@ SdfLayerRefPtr MayaUsdProxyShapeBase::computeSessionLayer(MDataBlock& dataBlock) { if (LayerManager::supportedNodeType(MPxNode::typeId())) { auto sessionLayerName = dataBlock.inputValue(sessionLayerNameAttr).asString(); - return LayerManager::findLayer(UsdMayaUtil::convert(sessionLayerName)); + auto mayaRef = getMayaReferenceOrigin(*this); + const MFnReference* fromReference = mayaRef ? mayaRef.get() : nullptr; + return LayerManager::findLayer(UsdMayaUtil::convert(sessionLayerName), fromReference); } else { return nullptr; } @@ -824,7 +849,9 @@ MStatus MayaUsdProxyShapeBase::computeInStageDataCached(MDataBlock& dataBlock) UsdStageRefPtr finalUsdStage; SdfPath primPath; - MayaUsd::LayerNameMap layerNameMap = LayerManager::getLayerNameMap(); + auto mayaRef = getMayaReferenceOrigin(*this); + const MFnReference* fromReference = mayaRef ? mayaRef.get() : nullptr; + MayaUsd::LayerNameMap layerNameMap = LayerManager::getLayerNameMap(fromReference); MDataHandle inDataHandle = dataBlock.inputValue(inStageDataAttr, &retValue); CHECK_MSTATUS_AND_RETURN_IT(retValue); @@ -844,7 +871,7 @@ MStatus MayaUsdProxyShapeBase::computeInStageDataCached(MDataBlock& dataBlock) VtArray updatedReferences; for (const auto& identifier : referencedLayers) { // Update the identifier reference in the customer layer data - auto layer = LayerManager::findLayer(identifier); + auto layer = LayerManager::findLayer(identifier, fromReference); if (layer) { updatedReferences.push_back(layer->GetIdentifier()); } diff --git a/test/lib/CMakeLists.txt b/test/lib/CMakeLists.txt index 7f8f13c568..bd81670213 100644 --- a/test/lib/CMakeLists.txt +++ b/test/lib/CMakeLists.txt @@ -2,6 +2,7 @@ set(TEST_SCRIPT_FILES testMayaUsdConverter.py testMayaUsdCreateStageCommands.py + testMayaUsdCreateStageInMayaRef.py testMayaUsdDirtyScene.py testMayaUsdLayerEditorCommands.py testMayaUsdProxyAccessor.py diff --git a/test/lib/testMayaUsdCreateStageInMayaRef.py b/test/lib/testMayaUsdCreateStageInMayaRef.py new file mode 100644 index 0000000000..e5f01ad146 --- /dev/null +++ b/test/lib/testMayaUsdCreateStageInMayaRef.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python + +# +# Copyright 2020 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. +# + +import unittest +import os.path + +import fixturesUtils +from ufeUtils import ufeFeatureSetVersion + +from maya import cmds +from maya import OpenMaya as om +from maya import standalone +import maya.mel as mel + +import mayaUsd.lib +import mayaUsd.ufe +import mayaUsd_createStageWithNewLayer + + +class MayaUsdCreateStageInMayaRefTestCase(unittest.TestCase): + """ + Test reloading a Maya scene that contains a Maya reference which contain a USD stage. + """ + + @classmethod + def setUpClass(cls): + fixturesUtils.setUpClass(__file__) + cmds.loadPlugin('mayaUsdPlugin') + + @classmethod + def tearDownClass(cls): + standalone.uninitialize() + + def testCreateStageInMayaRef(self): + ''' + Test reloading a Maya scene that contains a Maya reference which contain a USD stage. + ''' + testDir = os.path.join(os.path.abspath('.'),'testMayaUsdCreateStageInMayaRef') + + # Note: for some reason, when Maya processes file path for Maya references, + # it really does not like backward slashes on Windows. So make sure + # all paths use forward slashes. + usd_cube_scene = os.path.join(testDir, "cube.usd").replace('\\', '/') + maya_with_usd_ref = os.path.join(testDir, "maya_with_usd_ref.ma").replace('\\', '/') + maya_with_maya_ref = os.path.join(testDir, "maya_with_maya_ref.ma").replace('\\', '/') + + # Create a USD scene with a cube + cmds.file(new=True, force=True) + cmds.polyCube(name="cube1") + USD_EXPORT_SETTING = 1 + cmds.optionVar(iv=("mayaUsd_SerializedUsdEditsLocation", USD_EXPORT_SETTING)) + cmds.file(usd_cube_scene, exportAll=True, force=True, type="USD Export") + + # Create a Maya scene with an empty USD stage + cmds.file(new=True, force=True) + proxy_shape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + stage = mayaUsd.lib.GetPrim(proxy_shape).GetStage() + self.assertTrue(stage) + + # Add a reference to the usd scene with the cube + usdCubePath = "/usd_cube" + ref_prim = stage.DefinePrim(usdCubePath) + self.assertTrue(ref_prim) + ref_prim.GetReferences().AddReference(usd_cube_scene) + + # Save the scene + cmds.optionVar(iv=("mayaUsd_SerializedUsdEditsLocation", USD_EXPORT_SETTING)) + om.MFileIO.saveAs(maya_with_usd_ref, "mayaAscii") + + # Make sure we don't retain references to USD data. + stage = None + ref_prim = None + + # Reference the Maya scene in a new scene + namespace = 'my_ref' + cmds.file(new=True, force=True) + cmds.file(maya_with_usd_ref, reference=True, namespace=namespace) + om.MFileIO.saveAs(maya_with_maya_ref, "mayaAscii") + + cmds.file(new=True, force=True) + cmds.file(maya_with_maya_ref, open=True) + + proxy_shape_in_ref = proxy_shape.replace('|', '|%s:' % namespace) + + stage = mayaUsd.lib.GetPrim(proxy_shape_in_ref).GetStage() + self.assertTrue(stage) + ref_prim = stage.DefinePrim(usdCubePath) + self.assertTrue(ref_prim)