From 201d51346d68fb2b273ec1c9fd33328181fc5425 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Wed, 27 Oct 2021 11:14:14 -0400 Subject: [PATCH 01/12] Tremblp/maya 112824/pull push 01 (#254) * Second demo * Added marking menu support for USD and DAG objects * Only show USD marking menu for pulled DAG objects * Added push animation support and push to USD marking menu * Bake animation only when pulled object is animated * Import MayaReference under common parent. Fix pull of MayaReference. * Move pull/pushBack workflow to updater. Python script is still available, but soon we should migrate the rest of the code * Added missing export symbols. * WIP - Shot based animation * Extend registry to support look-up by maya type * WIP - Shot based animation * Support for pulled object directly under proxy shape * Don't show pull menu when no USD prims are selected * WIP - Shot based animation * Added new PullObjectHierarchy to handle pickwalking. * MayaReference updater called from manager * Fix baking * Clang * WIP - Shot based animation * Hide Maya pulled object. * Remove complex Maya path stuff. * New UsdPathMappingHandler * Implement copy spec in mayareference updater * Implemented maya reference push back to payload in cache variant * WIP - Shot based animation * Register for new callback "addItemsToOutlinerNodePopupMenu" to add context menu items to Maya objects. * Moved "USD Layer Editor" context menu (on proxy) to plugin (from Maya). * AutoPull for updaters that declare a need for it * CopyBetween data models. Old script is now pretty much replaced. * WIP - Shot based animation * Register/unregister a Maya path mapping handler with the core library. * Refactor some common code in {Usd/ProxyShape}Hierarchy. * Optimize the path mapping handler with a cache and check ancestors of input host path. * Invisibile prims don't get notifications on _PropagateDirtyBits or Sync, so when a prim turns invisible make sure all reprs can draw correctly. * Clang format. * MAYA-111521: Fix error messages showing up when opening the "Applied Schema" category. * Basis Curves have the same issue meshes do. Invisible prims get no chances to update, so make sure all reprs will draw correctly when the prim turns invisible. * Update SchemaRegistry callsite for a USD function name change * Updating the build doc with the latest supported version of USD dev * Disable the new point snapping support for the time being. * Implement setMatrixCmd() for common transform API. * MAYA-112058 - AE Missing Information on Materials and texture file prims USD v21.02 in Maya 2022.1 * In Maya 2022.1 we need to hold onto the Ufe SceneItem to make sure it doesn't go stale. This is not needed in latest Maya. * MAYA-112126: make sure parented object is added to selection list after it is moved. * Address feedbacks. * MAYA-111835: Fix Grouping a prim twice that crashed Maya. * check for return status when calling MGlobal::executeCommand. * Simplify selection logic. * Revert "MAYA-112126: make sure parented object is added to selection list after it is moved." * Update ae_template.py Python 2 strings are ascii by default, whereas Python 2 strings as are unicode by default. This string had a unicode char (for apostrophe) that I replaced with the ascii one. * Working Dag pull / push with pull root and pull parent. * Parent transform onto pull parent, remove pull root on last child, and hide it in Outliner. * Update to latest changes from Krystian. * Node lock / unlock on pull / push, and copy operation support. * WIP - Shot based animation * Fixed crash from UFE test. * Respect edit target * Use new export roots implementation. * Ufe PathMappingHandler is now in UFE v3 * clang format * Small fix for Outliner callback * Fixing bad merge of test. * Two-step pull, pull clear, design wording and icons, and tests. (#251) * Two-step pull, pull clear, design wording and icons, and tests. * Addressed code review comments. * Addressed test code review feedback. * Edit as Maya tests conditional to UFE path mapper. * Edit as Maya depends on UFE v3 path mapping. * More edit as Maya dependence on UFE v3 path mapping. * More conditional compilation of edit as Maya code. * Yet more conditional compilation of edit as Maya code. * Conditional compilation of edit as Maya code part 6. * Conditional compilation of edit as Maya code part 7. * Conditional compilation of edit as Maya code part 8. * Conditional compilation of edit as Maya code part 9. * WIP: layer traversal on push copy specs. * Prim updater push copy spec code cleanup. * Production-quality changes for push traversal. * WIP changes for push traversal. * Split out DiscardEdits and PushEnd. * Layer traversal error reporting and discard edits fix. * Prim updater cleanup. * Debugging output cleanup. Co-authored-by: Krystian Ligenza Co-authored-by: Sean Donnelly <23455376+seando-adsk@users.noreply.github.com> Co-authored-by: krickw Co-authored-by: Hamed Sabri Co-authored-by: Dan McGarry --- lib/mayaUsd/commands/baseExportCommand.cpp | 3 +- lib/mayaUsd/fileio/CMakeLists.txt | 33 +- lib/mayaUsd/fileio/fallbackPrimUpdater.cpp | 25 + lib/mayaUsd/fileio/fallbackPrimUpdater.h | 42 + lib/mayaUsd/fileio/inMemoryTranslation.py | 325 +++++ lib/mayaUsd/fileio/jobs/jobArgs.cpp | 3 +- lib/mayaUsd/fileio/jobs/readJob.cpp | 6 + lib/mayaUsd/fileio/jobs/readJob.h | 3 + lib/mayaUsd/fileio/jobs/writeJob.cpp | 14 + lib/mayaUsd/fileio/jobs/writeJob.h | 6 + lib/mayaUsd/fileio/primUpdater.cpp | 254 +++- lib/mayaUsd/fileio/primUpdater.h | 81 +- lib/mayaUsd/fileio/primUpdaterArgs.cpp | 59 + lib/mayaUsd/fileio/primUpdaterArgs.h | 59 + lib/mayaUsd/fileio/primUpdaterContext.cpp | 22 +- lib/mayaUsd/fileio/primUpdaterContext.h | 33 +- lib/mayaUsd/fileio/primUpdaterManager.cpp | 1110 +++++++++++++++++ lib/mayaUsd/fileio/primUpdaterManager.h | 94 ++ lib/mayaUsd/fileio/primUpdaterRegistry.cpp | 81 +- lib/mayaUsd/fileio/primUpdaterRegistry.h | 48 +- lib/mayaUsd/listeners/proxyShapeNotice.cpp | 14 + lib/mayaUsd/listeners/proxyShapeNotice.h | 16 + lib/mayaUsd/nodes/proxyAccessor.py | 15 +- lib/mayaUsd/nodes/proxyShapeBase.cpp | 1 + lib/mayaUsd/python/CMakeLists.txt | 8 + lib/mayaUsd/python/module.cpp | 8 + lib/mayaUsd/python/wrapPrimUpdaterManager.cpp | 102 ++ lib/mayaUsd/resources/icons/CMakeLists.txt | 3 + .../resources/icons/discard_edits_100.png | Bin 0 -> 393 bytes .../resources/icons/discard_edits_150.png | Bin 0 -> 454 bytes .../resources/icons/discard_edits_200.png | Bin 0 -> 606 bytes .../resources/icons/edit_as_Maya_100.png | Bin 0 -> 425 bytes .../resources/icons/edit_as_Maya_150.png | Bin 0 -> 545 bytes .../resources/icons/edit_as_Maya_200.png | Bin 0 -> 749 bytes .../resources/icons/merge_to_USD_100.png | Bin 0 -> 358 bytes .../resources/icons/merge_to_USD_150.png | Bin 0 -> 542 bytes .../resources/icons/merge_to_USD_200.png | Bin 0 -> 642 bytes lib/mayaUsd/ufe/CMakeLists.txt | 6 + lib/mayaUsd/ufe/Global.cpp | 21 + lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp | 26 +- lib/mayaUsd/ufe/PulledObjectHierarchy.cpp | 133 ++ lib/mayaUsd/ufe/PulledObjectHierarchy.h | 95 ++ .../ufe/PulledObjectHierarchyHandler.cpp | 75 ++ .../ufe/PulledObjectHierarchyHandler.h | 57 + lib/mayaUsd/ufe/UsdContextOps.cpp | 20 +- lib/mayaUsd/ufe/UsdHierarchy.cpp | 20 +- lib/mayaUsd/ufe/UsdPathMappingHandler.cpp | 131 ++ lib/mayaUsd/ufe/UsdPathMappingHandler.h | 54 + lib/mayaUsd/ufe/UsdUIUfeObserver.cpp | 12 + lib/mayaUsd/ufe/Utils.cpp | 37 +- lib/mayaUsd/ufe/Utils.h | 14 + lib/mayaUsd/utils/CMakeLists.txt | 2 + lib/mayaUsd/utils/traverseLayer.cpp | 113 ++ lib/mayaUsd/utils/traverseLayer.h | 74 ++ lib/mayaUsd/utils/util.cpp | 20 +- lib/mayaUsd/utils/util.h | 4 - lib/usd/translators/CMakeLists.txt | 11 +- lib/usd/translators/mayaReferenceReader.cpp | 21 +- lib/usd/translators/mayaReferenceUpdater.cpp | 62 +- lib/usd/translators/mayaReferenceUpdater.h | 14 +- plugin/adsk/plugin/exportTranslator.cpp | 3 +- plugin/adsk/plugin/plugin.cpp | 9 + plugin/adsk/scripts/CMakeLists.txt | 1 + plugin/adsk/scripts/USDMenuProc.mel | 50 + .../adsk/scripts/mayaUSDRegisterStrings.mel | 1 + plugin/adsk/scripts/mayaUsdMenu.mel | 149 +++ .../adsk/scripts/mayaUsd_pluginUICreation.mel | 2 + .../pxr/maya/lib/usdMaya/exportTranslator.cpp | 3 +- test/lib/mayaUsd/fileio/CMakeLists.txt | 10 + test/lib/mayaUsd/fileio/testDiscardEdits.py | 106 ++ test/lib/mayaUsd/fileio/testEditAsMaya.py | 113 ++ test/lib/mayaUsd/fileio/testMergeToUsd.py | 123 ++ test/lib/ufe/testGroupCmd.py | 8 +- test/testUtils/mayaUtils.py | 11 + test/testUtils/usdUtils.py | 38 +- 75 files changed, 3961 insertions(+), 156 deletions(-) create mode 100644 lib/mayaUsd/fileio/fallbackPrimUpdater.cpp create mode 100644 lib/mayaUsd/fileio/fallbackPrimUpdater.h create mode 100644 lib/mayaUsd/fileio/inMemoryTranslation.py create mode 100644 lib/mayaUsd/fileio/primUpdaterArgs.cpp create mode 100644 lib/mayaUsd/fileio/primUpdaterArgs.h create mode 100644 lib/mayaUsd/fileio/primUpdaterManager.cpp create mode 100644 lib/mayaUsd/fileio/primUpdaterManager.h create mode 100644 lib/mayaUsd/python/wrapPrimUpdaterManager.cpp create mode 100644 lib/mayaUsd/resources/icons/discard_edits_100.png create mode 100644 lib/mayaUsd/resources/icons/discard_edits_150.png create mode 100644 lib/mayaUsd/resources/icons/discard_edits_200.png create mode 100644 lib/mayaUsd/resources/icons/edit_as_Maya_100.png create mode 100644 lib/mayaUsd/resources/icons/edit_as_Maya_150.png create mode 100644 lib/mayaUsd/resources/icons/edit_as_Maya_200.png create mode 100644 lib/mayaUsd/resources/icons/merge_to_USD_100.png create mode 100644 lib/mayaUsd/resources/icons/merge_to_USD_150.png create mode 100644 lib/mayaUsd/resources/icons/merge_to_USD_200.png create mode 100644 lib/mayaUsd/ufe/PulledObjectHierarchy.cpp create mode 100644 lib/mayaUsd/ufe/PulledObjectHierarchy.h create mode 100644 lib/mayaUsd/ufe/PulledObjectHierarchyHandler.cpp create mode 100644 lib/mayaUsd/ufe/PulledObjectHierarchyHandler.h create mode 100644 lib/mayaUsd/ufe/UsdPathMappingHandler.cpp create mode 100644 lib/mayaUsd/ufe/UsdPathMappingHandler.h create mode 100644 lib/mayaUsd/utils/traverseLayer.cpp create mode 100644 lib/mayaUsd/utils/traverseLayer.h create mode 100644 plugin/adsk/scripts/USDMenuProc.mel create mode 100644 test/lib/mayaUsd/fileio/testDiscardEdits.py create mode 100644 test/lib/mayaUsd/fileio/testEditAsMaya.py create mode 100644 test/lib/mayaUsd/fileio/testMergeToUsd.py diff --git a/lib/mayaUsd/commands/baseExportCommand.cpp b/lib/mayaUsd/commands/baseExportCommand.cpp index 003906d6b0..4ee4dc1484 100644 --- a/lib/mayaUsd/commands/baseExportCommand.cpp +++ b/lib/mayaUsd/commands/baseExportCommand.cpp @@ -319,8 +319,7 @@ MStatus MayaUSDExportCommand::doIt(const MArgList& args) std::string rootPath = tmpArgList.asString(0).asChar(); if (!rootPath.empty()) { - MDagPath rootDagPath; - UsdMayaUtil::GetDagPathByName(rootPath, rootDagPath); + MDagPath rootDagPath = UsdMayaUtil::nameToDagPath(rootPath); if (!rootDagPath.isValid()) { MGlobal::displayError( MString("Invalid dag path provided for exportRoot: ") diff --git a/lib/mayaUsd/fileio/CMakeLists.txt b/lib/mayaUsd/fileio/CMakeLists.txt index 641c664c0b..2348380e89 100644 --- a/lib/mayaUsd/fileio/CMakeLists.txt +++ b/lib/mayaUsd/fileio/CMakeLists.txt @@ -12,9 +12,6 @@ target_sources(${PROJECT_NAME} primReaderArgs.cpp primReaderContext.cpp primReaderRegistry.cpp - primUpdater.cpp - primUpdaterContext.cpp - primUpdaterRegistry.cpp primWriter.cpp primWriterArgs.cpp primWriterContext.cpp @@ -28,6 +25,19 @@ target_sources(${PROJECT_NAME} writeJobContext.cpp ) +# Edit as Maya requires UFE path mapping. +if(CMAKE_UFE_V3_FEATURES_AVAILABLE) + target_sources(${PROJECT_NAME} + PRIVATE + fallbackPrimUpdater.cpp + primUpdater.cpp + primUpdaterArgs.cpp + primUpdaterContext.cpp + primUpdaterRegistry.cpp + primUpdaterManager.cpp + ) +endif() + set(HEADERS fallbackPrimReader.h functorPrimReader.h @@ -38,9 +48,6 @@ set(HEADERS primReaderArgs.h primReaderContext.h primReaderRegistry.h - primUpdater.h - primUpdaterContext.h - primUpdaterRegistry.h primWriter.h primWriterArgs.h primWriterContext.h @@ -54,6 +61,17 @@ set(HEADERS writeJobContext.h ) +if(CMAKE_UFE_V3_FEATURES_AVAILABLE) + list(APPEND HEADERS + fallbackPrimUpdater.h + primUpdater.h + primUpdaterArgs.h + primUpdaterContext.h + primUpdaterRegistry.h + primUpdaterManager.h + ) +endif() + # ----------------------------------------------------------------------------- # promoted headers # ----------------------------------------------------------------------------- @@ -66,6 +84,9 @@ install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${PROJECT_NAME}/fileio ) +set(PYTHON_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/lib/python/${PROJECT_NAME}/lib) +install(FILES inMemoryTranslation.py DESTINATION ${PYTHON_INSTALL_PREFIX}) + # ----------------------------------------------------------------------------- # subdirectories # ----------------------------------------------------------------------------- diff --git a/lib/mayaUsd/fileio/fallbackPrimUpdater.cpp b/lib/mayaUsd/fileio/fallbackPrimUpdater.cpp new file mode 100644 index 0000000000..1c6de03f87 --- /dev/null +++ b/lib/mayaUsd/fileio/fallbackPrimUpdater.cpp @@ -0,0 +1,25 @@ +// +// 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 "fallbackPrimUpdater.h" + +PXR_NAMESPACE_OPEN_SCOPE + +FallbackPrimUpdater::FallbackPrimUpdater(const MFnDependencyNode& depNodeFn, const Ufe::Path& path) + : UsdMayaPrimUpdater(depNodeFn, path) +{ +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/fileio/fallbackPrimUpdater.h b/lib/mayaUsd/fileio/fallbackPrimUpdater.h new file mode 100644 index 0000000000..50f0b2316a --- /dev/null +++ b/lib/mayaUsd/fileio/fallbackPrimUpdater.h @@ -0,0 +1,42 @@ +// +// 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 PXRUSDMAYA_FALLBACK_PRIMUPDATER_H +#define PXRUSDMAYA_FALLBACK_PRIMUPDATER_H + +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + +class FallbackPrimUpdater : public UsdMayaPrimUpdater +{ +public: + MAYAUSD_CORE_PUBLIC + FallbackPrimUpdater(const MFnDependencyNode& depNodeFn, const Ufe::Path& path); + + // clang errors if you use "= default" here, due to const SdfPath member + // see: http://open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#253 + // ...which it seems clang only implements if using newer version + cpp std + FallbackPrimUpdater() { } + + virtual ~FallbackPrimUpdater() = default; + +private: +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif diff --git a/lib/mayaUsd/fileio/inMemoryTranslation.py b/lib/mayaUsd/fileio/inMemoryTranslation.py new file mode 100644 index 0000000000..066c58c4e4 --- /dev/null +++ b/lib/mayaUsd/fileio/inMemoryTranslation.py @@ -0,0 +1,325 @@ +import maya.cmds as cmds +import maya.api.OpenMaya as om +import maya.api.OpenMayaAnim as oma +import mayaUsd.lib as mayaUsdLib +from mayaUsd.lib import proxyAccessor as pa +import ufe +import re + +from pxr import Usd, Sdf + +# --------------------------------- UTILITIES ---------------------------------------------- +def isAnimated(dagPath): + if cmds.evaluationManager( query=True, invalidate=True ): + upstream = cmds.evaluationManager(ust=dagPath) + countNonProxyShape = len(upstream) + for node in upstream: + if cmds.nodeType(node) == 'mayaUsdProxyShape': + countNonProxyShape -= 1 + print(countNonProxyShape) + return countNonProxyShape > 0 + else: + sel_list = om.MSelectionList() + sel_list.add(dagPath) + dep_node = sel_list.getDependNode(0) + return oma.MAnimUtil.isAnimated(dep_node) + +def pullSetName(): + return 'pullStateSet' + +def pullPrimMetadataKey(): + return 'Maya:Pull:DagPath' + +def pullDGMetadataKey(): + return 'Pull_UfePath' + +def dagPathToSdfPath(longDagPath): + path = re.sub(r'[|]',r'/',longDagPath) + sdfPath = Sdf.Path(re.sub(r'[^a-zA-Z0-9_/]',r'_',path)) + return sdfPath + +def sdfPathToDagPath(sdfPath): + dagPath = re.sub(r'/',r'|',sdfPath) + return dagPath + +def persistPullInformation(srcUfePath,dstDagPath): + setName = pullSetName() + + if setName not in cmds.listSets(allSets=True): + cmds.sets(em=True,n=setName) + + cmds.sets([dstDagPath],add=setName) + + # Store on src prim as custom metadata + shapePath, srcSdfPath = pa.getDagAndPrimFromUfe(srcUfePath) + stage = mayaUsdLib.GetPrim(shapePath).GetStage() + srcPrim = stage.GetPrimAtPath(srcSdfPath) + srcPrim.SetCustomDataByKey(pullPrimMetadataKey(), dstDagPath) + # Store on dst dg node as metadata + if not cmds.attributeQuery(pullDGMetadataKey(), node=dstDagPath, exists=True): + cmds.addAttr(dstDagPath,longName=pullDGMetadataKey(), dataType='string') + cmds.setAttr('{}.{}'.format(dstDagPath, pullDGMetadataKey()), ufe.PathString.string(srcUfePath.path()), type='string') + +def addExcludeFromRendering(shapePath, sdfPath): + excludedPrimsString = cmds.getAttr('{}.excludePrimPaths'.format(shapePath)) or '' + excludedPrims = excludedPrimsString.split(',') + excludedPrims.insert(0,sdfPath) + excludedPrimsString = ','.join(set(excludedPrims)) + cmds.setAttr('{}.excludePrimPaths'.format(shapePath), excludedPrimsString, type='string') + +def removeExcludeFromRendering(shapePath, sdfPath): + excludedPrimsString = cmds.getAttr('{}.excludePrimPaths'.format(shapePath)) or '' + excludedPrims = excludedPrimsString.split(',') + if sdfPath in excludedPrims: + excludedPrims.remove(sdfPath) + excludedPrimsString = ','.join(excludedPrims) + cmds.setAttr('{}.excludePrimPaths'.format(shapePath), excludedPrimsString, type='string') + +def getPullInformationFromDag(dagPath): + setName = pullSetName() + + if setName not in cmds.listSets(allSets=True): + return None + + if not cmds.sets([dagPath],im=setName): + return None + + if not cmds.attributeQuery(pullDGMetadataKey(), node=dagPath, exists=True): + print('Error - DAG path is in pulled set but does not have pulled path stored') + return None + + pulledUfePathString = cmds.getAttr('{}.{}'.format(dagPath,pullDGMetadataKey())) or '' + + pulledUfePath = ufe.PathString.path(pulledUfePathString) + pulledUfeItem = ufe.Hierarchy.createItem(pulledUfePath) + + return pulledUfeItem + +def getPullInformationFromUsd(ufePath): + shapePath, sdfPath = pa.getDagAndPrimFromUfe(ufePath) + if not (shapePath and sdfPath): + return None + + stage = mayaUsdLib.GetPrim(shapePath).GetStage() + srcPrim = stage.GetPrimAtPath(sdfPath) + pulledDagPath = srcPrim.GetCustomDataByKey(pullPrimMetadataKey()) + + return pulledDagPath + +def getPullInformationFromPrim(usdPrim): + pulledDagPath = usdPrim.GetCustomDataByKey(pullPrimMetadataKey()) + + return pulledDagPath + +# --------------------------------- PUSH & PULL ---------------------------------------------- +def pull(ufePath, srcLayer, asCopy=False): + shapePath, sdfPath = pa.getDagAndPrimFromUfe(ufePath) + + if shapePath == None or sdfPath == None: + return + + if srcLayer == None: + srcLayer = mayaUsdLib.GetPrim(shapePath).GetStage().GetRootLayer() + + if sdfPath == None: + sdfPath = Sdf.Path.absoluteRootPath + + cmds.mayaUSDImport(f=srcLayer.identifier, primPath=sdfPath, readAnimData=True) + + # put objects in the right space + parentPath = Sdf.Path(sdfPath).GetParentPath() + childDagPath = sdfPathToDagPath('|'+Sdf.Path(sdfPath).name) + ufeChildren = [pa.createUfeSceneItem(childDagPath)] + if parentPath != Sdf.Path.absoluteRootPath: + ufeParent = pa.createUfeSceneItem(shapePath,parentPath.pathString) + else: + ufeParent = pa.createUfeSceneItem(shapePath) + pa.parentItems(ufeChildren, ufeParent) + + if not asCopy: + #store the linkage between prim and dag in metadata + persistPullInformation(ufePath,childDagPath) + + #prevent doouble rendering by excluding pulled prim from rendering + addExcludeFromRendering(shapePath, sdfPath) + + return childDagPath + +def pushClear(ufePush): + pulledShapePath = None + pulledPrim = None + pushDagPath = None + + if pa.isUfeUsdPath(ufePush): + pulledShapePath, pulledSdfPathString = pa.getDagAndPrimFromUfe(ufePush) + pulledStage = mayaUsdLib.GetPrim(pulledShapePath).GetStage() + pulledPrim = pulledStage.GetPrimAtPath(pulledSdfPathString) + pushDagPath = pulledPrim.GetCustomDataByKey(pullPrimMetadataKey()) + else: + pushDagPath, pushSdfPath = pa.getDagAndPrimFromUfe(ufePush) + pulledUfePath = getPullInformationFromDag(pushDagPath) + pulledShapePath, pulledSdfPathString = pa.getDagAndPrimFromUfe(pulledUfePath) + + pulledStage = mayaUsdLib.GetPrim(pulledShapePath).GetStage() + pulledPrim = pulledStage.GetPrimAtPath(pulledSdfPathString) + + # Cleanup pull infromation and re-enable rendering from stage + removeExcludeFromRendering(pulledShapePath,pulledPrim.GetPath().pathString) + cmds.delete(pushDagPath) + pulledPrim.ClearCustomDataByKey(pullPrimMetadataKey()) + +def push(dagPath, dstLayer, dstSdfPath=None, withAnimation=True): + if dstSdfPath == Sdf.Path.absoluteRootPath: + print('Pushing state over pseudo root is not allowed') + return False + + srcLayer = Sdf.Layer.CreateAnonymous() + cmds.select(dagPath); + if withAnimation and isAnimated(dagPath): + cmds.mayaUSDExport(f=srcLayer.identifier, sl=True, frameSample=1.0, frameRange=[cmds.playbackOptions(q=True, min=True),cmds.playbackOptions(q=True, max=True)]) + else: + cmds.mayaUSDExport(f=srcLayer.identifier, sl=True) + + stage = Usd.Stage.Open(srcLayer) + srcPrim = stage.GetPrimAtPath(dagPathToSdfPath(dagPath)) + if dstSdfPath: + Sdf.CopySpec(srcLayer, srcPrim.GetPath(), dstLayer, dstSdfPath) + else: + Sdf.CopySpec(srcLayer, srcPrim.GetPath(), dstLayer, srcPrim.GetPath()) + + # Copy everything (not used anymore...left here simply because this script is not yet under source control) + #root = stage.GetPrimAtPath(Sdf.Path.absoluteRootPath) + #for child in root.GetAllChildren(): + # Sdf.CopySpec(srcLayer, child.GetPath(), dstLayer, child.GetPath()) + + return True + +def pushBack(ufePush): + pushDagPath, pushSdfPath = pa.getDagAndPrimFromUfe(ufePush) + if pushSdfPath: + print('Not implemented pull from usd selection - but we have everything stored to make it') + else: + pulledUfePath = getPullInformationFromDag(pushDagPath) + pulledShapePath, pulledSdfPathString = pa.getDagAndPrimFromUfe(pulledUfePath) + + pulledStage = mayaUsdLib.GetPrim(pulledShapePath).GetStage() + + dstLayer = pulledStage.GetRootLayer() + + if push(pushDagPath,dstLayer,Sdf.Path(pulledSdfPathString)): + pulledPrim = pulledStage.GetPrimAtPath(pulledSdfPathString) + # Cleanup pull infromation and re-enable rendering from stage + removeExcludeFromRendering(pulledShapePath,pulledSdfPathString) + cmds.delete(pushDagPath) + # Since we pushed over existing prim, the custom metadata should be already cleared + pulledPrim.ClearCustomDataByKey(pullPrimMetadataKey()) + +def pushBetween(ufeSrc,ufeDst): + if pa.isUfeUsdPath(ufeSrc): + print('Must select first Dag object') + return + + srcDagPath, srcSdfPath = pa.getDagAndPrimFromUfe(ufeSrc) + shapePath, dstSdfPath = pa.getDagAndPrimFromUfe(ufeDst) + + dstLayer = mayaUsdLib.GetPrim(shapePath).GetStage().GetRootLayer() + + if dstSdfPath and dstSdfPath != Sdf.Path.absoluteRootPath: + push(srcDagPath, dstLayer, Sdf.Path(dstSdfPath)) + else: + push(srcDagPath, dstLayer) + +def pushToVariant(srcDagPath, dstSdfPath, dstLayer, dstVariantSetName, dstVariantName): + srcLayer = Sdf.Layer.CreateAnonymous() + cmds.mayaUSDExport(f=srcLayer.identifier, sl=True) + + dstStage = Usd.Stage.Open(dstLayer) + dstPrim = dstStage.GetPrimAtPath(dstSdfPath) + dstVariantSet = dstPrim.GetVariantSets().AddVariantSet(dstVariantSetName) + dstVariantSet.AddVariant(dstVariantName) + dstVariantSet.SetVariantSelection(dstVariantName) + with dstVariantSet.GetVariantEditContext(): + stage = Usd.Stage.Open(srcLayer) + root = stage.GetPrimAtPath(Sdf.Path.absoluteRootPath) + for child in root.GetAllChildren(): + #targetPrimPath = dstPrim.GetPath().AppendPath(dstVariantName) + #dstStage.DefinePrim(targetPrimPath, 'Scope') + dstPrimPathWithVariant = dstPrim.GetPath().AppendVariantSelection(dstVariantSetName, dstVariantName) + dstPrimPathWithVariant = child.GetPath().ReplacePrefix(Sdf.Path.absoluteRootPath,dstPrimPathWithVariant) + print(dstPrimPathWithVariant) + Sdf.CopySpec(srcLayer, child.GetPath(), dstLayer, dstPrimPathWithVariant) + +# -------------------------------------- WORKFLOWS ------------------------------------------ + +def pullSelection(): + pulledDagPath = pull(pa.getUfeSelection(),None) or '' + cmds.select(pulledDagPath) + +def pushSelection(): + ufeSelection = ufe.GlobalSelection.get() + if len(ufeSelection) == 2: + ufeSelectionIter = iter(ufeSelection) + + ufeSrc = next(ufeSelectionIter) + ufeDst = next(ufeSelectionIter) + + pushBetween(ufeSrc,ufeDst) + elif len(ufeSelection) == 1: + ufePush = next(iter(ufeSelection)) + pushBack(ufePush) + else: + print('Unhandled push from selection') + +def pushClearSelection(): + ufeSelection = ufe.GlobalSelection.get() + if len(ufeSelection) == 1: + ufePush = next(iter(ufeSelection)) + pushClear(ufePush) + else: + print('Unhandled push clear from selection') + +def pushSelectionToVariant(variantSetName, variantName): + ufeSelection = ufe.GlobalSelection.get() + if len(ufeSelection) != 2: + print('Must select exactly two objects') + return + + ufeSelectionIter = iter(ufeSelection) + + ufeSrc = next(ufeSelectionIter) + ufeDst = next(ufeSelectionIter) + + ufeSelectionIter = None + + if pa.isUfeUsdPath(ufeSrc): + print('Must select first Dag object') + return + + if not pa.isUfeUsdPath(ufeDst): + print('Must select second ufe-usd object') + return + + srcDagPath, srcSdfPath = pa.getDagAndPrimFromUfe(ufeSrc) + shapePath, dstSdfPath = pa.getDagAndPrimFromUfe(ufeDst) + + dstLayer = mayaUsdLib.GetPrim(shapePath).GetStage().GetRootLayer() + + pushToVariant(srcDagPath, dstSdfPath, dstLayer, variantSetName, variantName) + +def assignMaterialToSelection(material): + ufeItem = pa.getUfeSelection() + shapePath, sdfPath = pa.getDagAndPrimFromUfe(ufeItem) + + tmpDagPath = sdfPathToDagPath(sdfPath) + + pull(ufeItem,None) + + cmds.select(tmpDagPath); + cmds.sets(e=True,forceElement=material) + cmds.select(cl=True) + + dstLayer = mayaUsdLib.GetPrim(shapePath).GetStage().GetRootLayer() + push(tmpDagPath, dstLayer) + + cmds.delete(tmpDagPath) + diff --git a/lib/mayaUsd/fileio/jobs/jobArgs.cpp b/lib/mayaUsd/fileio/jobs/jobArgs.cpp index 3d4c3ffd37..35c1fdb4bf 100644 --- a/lib/mayaUsd/fileio/jobs/jobArgs.cpp +++ b/lib/mayaUsd/fileio/jobs/jobArgs.cpp @@ -309,8 +309,7 @@ static PcpMapFunction::PathMap _ExportRootsMap( const std::vector exportRoots = _Vector(userArgs, key); for (const std::string& rootPath : exportRoots) { if (!rootPath.empty()) { - MDagPath rootDagPath; - UsdMayaUtil::GetDagPathByName(rootPath, rootDagPath); + MDagPath rootDagPath = UsdMayaUtil::nameToDagPath(rootPath); addExportRootPathPairFn(rootDagPath); } else { includeEntireSelection = true; diff --git a/lib/mayaUsd/fileio/jobs/readJob.cpp b/lib/mayaUsd/fileio/jobs/readJob.cpp index a7ec2bb95c..8f1df95592 100644 --- a/lib/mayaUsd/fileio/jobs/readJob.cpp +++ b/lib/mayaUsd/fileio/jobs/readJob.cpp @@ -589,6 +589,12 @@ const MDagPath& UsdMaya_ReadJob::GetMayaRootDagPath() const { return mMayaRootDa double UsdMaya_ReadJob::timeSampleMultiplier() const { return mTimeSampleMultiplier; } +const UsdMayaPrimReaderContext::ObjectRegistry& +UsdMaya_ReadJob::GetNewNodeRegistry() const +{ + return mNewNodeRegistry; +} + double UsdMaya_ReadJob::_setTimeSampleMultiplierFrom(const double layerFPS) { double sceneFPS = UsdMayaUtil::GetSceneMTimeUnitAsDouble(); diff --git a/lib/mayaUsd/fileio/jobs/readJob.h b/lib/mayaUsd/fileio/jobs/readJob.h index e968a64af3..7215d01149 100644 --- a/lib/mayaUsd/fileio/jobs/readJob.h +++ b/lib/mayaUsd/fileio/jobs/readJob.h @@ -69,6 +69,9 @@ class UsdMaya_ReadJob MAYAUSD_CORE_PUBLIC double timeSampleMultiplier() const; + MAYAUSD_CORE_PUBLIC + const UsdMayaPrimReaderContext::ObjectRegistry& GetNewNodeRegistry() const; + protected: // Types using _PrimReaderMap = std::unordered_map; diff --git a/lib/mayaUsd/fileio/jobs/writeJob.cpp b/lib/mayaUsd/fileio/jobs/writeJob.cpp index b35162d98f..523fe6cebc 100644 --- a/lib/mayaUsd/fileio/jobs/writeJob.cpp +++ b/lib/mayaUsd/fileio/jobs/writeJob.cpp @@ -78,6 +78,14 @@ UsdMaya_WriteJob::UsdMaya_WriteJob(const UsdMayaJobExportArgs& iArgs) UsdMaya_WriteJob::~UsdMaya_WriteJob() { } +SdfPath UsdMaya_WriteJob::MapDagPathToSdfPath(const MDagPath& dagPath) const +{ + SdfPath usdPrimPath; + TfMapLookup(mDagPathToUsdPathMap, dagPath, &usdPrimPath); + + return usdPrimPath; +} + /// Generates a name for a temporary usdc file in \p dir. /// Unless you are very, very unlucky, the stage name is unique because it's /// generated from a UUID. @@ -819,4 +827,10 @@ bool UsdMaya_WriteJob::_CheckNameClashes(const SdfPath& path, const MDagPath& da return true; } +const UsdMayaUtil::MDagPathMap& +UsdMaya_WriteJob::GetDagPathToUsdPathMap() const +{ + return mDagPathToUsdPathMap; +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/fileio/jobs/writeJob.h b/lib/mayaUsd/fileio/jobs/writeJob.h index e2ec861786..d51295447f 100644 --- a/lib/mayaUsd/fileio/jobs/writeJob.h +++ b/lib/mayaUsd/fileio/jobs/writeJob.h @@ -49,6 +49,12 @@ class UsdMaya_WriteJob MAYAUSD_CORE_PUBLIC bool Write(const std::string& fileName, bool append); + MAYAUSD_CORE_PUBLIC + SdfPath MapDagPathToSdfPath(const MDagPath& dagPath) const; + + MAYAUSD_CORE_PUBLIC + const UsdMayaUtil::MDagPathMap& GetDagPathToUsdPathMap() const; + private: /// Begins constructing the USD stage, writing out the values at the default /// time. Returns \c true if the stage can be created successfully. diff --git a/lib/mayaUsd/fileio/primUpdater.cpp b/lib/mayaUsd/fileio/primUpdater.cpp index 0d82457d3e..fa9c795f9e 100644 --- a/lib/mayaUsd/fileio/primUpdater.cpp +++ b/lib/mayaUsd/fileio/primUpdater.cpp @@ -16,30 +16,264 @@ // #include "primUpdater.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include #include +#include +#include +#include +#include +#include +#include +#include #include #include +#include +#include +#include + +#include + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +//------------------------------------------------------------------------------ +// Global variables +//------------------------------------------------------------------------------ +extern Ufe::Rtid g_MayaRtid; + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF PXR_NAMESPACE_OPEN_SCOPE -UsdMayaPrimUpdater::UsdMayaPrimUpdater(const MFnDependencyNode& depNodeFn, const SdfPath& usdPath) - : _dagPath(UsdMayaUtil::getDagPath(depNodeFn)) - , _mayaObject(depNodeFn.object()) - , _usdPath(usdPath) - , _baseDagToUsdPaths(UsdMayaUtil::getDagPathMap(depNodeFn, usdPath)) +namespace { +// Set name that will be used to hold all pulled objects +// MOVED +const std::string kPullSetName("pullStateSet"); + +// Metadata key used to store pull information on a prim +// MOVED +const TfToken kPullPrimMetadataKey("Maya:Pull:DagPath"); + +// Metadata key used to store pull information on a DG node +// MOVED +const MString kPullDGMetadataKey("Pull_UfePath"); + +Ufe::Path usdToMaya(const std::string& pathStr) +{ + Ufe::PathSegment seg( + Ufe::PathSegment::Components({ Ufe::PathComponent("world"), Ufe::PathComponent(pathStr) }), + MayaUsd::ufe::g_MayaRtid, + '|'); + return Ufe::Path(seg); +} + +} // namespace + +UsdMayaPrimUpdater::UsdMayaPrimUpdater(const MFnDependencyNode& depNodeFn, const Ufe::Path& path) + : _mayaObject(depNodeFn.object()) + , _path(path) +{ +} + +bool UsdMayaPrimUpdater::Pull(const UsdMayaPrimUpdaterContext& context) +{ + return true; +} + +bool UsdMayaPrimUpdater::DiscardEdits(const UsdMayaPrimUpdaterContext& context) +{ + MObject objectToDelete = GetMayaObject(); + if (!objectToDelete.isNull()) { + MGlobal::deleteNode(objectToDelete); + } + return true; +} + +bool UsdMayaPrimUpdater::PushEnd(const UsdMayaPrimUpdaterContext& context) { + return DiscardEdits(context); } -bool UsdMayaPrimUpdater::Push(UsdMayaPrimUpdaterContext* context) { return false; } +bool UsdMayaPrimUpdater::PushCopySpecs( + SdfLayerRefPtr srcLayer, + const SdfPath& topSrcPath, + SdfLayerRefPtr dstLayer, + const SdfPath& topDstPath) +{ + // Copy to the destination layer one primSpec at a time. The default + // SdfCopySpec(srcLayer, topSrcPath, dstLayer, topDstPath) overload + // recurses down to children, which doesn't allow for per-primSpec control. + // Call the SdfCopySpec overload with a functor that avoids recursing down + // to children, so that only the single top-level prim at that point in the + // traversal is copied. -bool UsdMayaPrimUpdater::Pull(UsdMayaPrimUpdaterContext* context) { return false; } + // Capture topSrcPath and topDstPath to pass them into SdfShouldCopyValue(). + // Since we're copying a single prim and not recursing to children, + // topSrcPath and srcPath will be equal, and so will topDstPath and dstPath. + auto shouldCopyValueFn = [topSrcPath, topDstPath]( + SdfSpecType specType, const TfToken& field, + const SdfLayerHandle& srcLayer, const SdfPath& srcPath, + bool fieldInSrc, + const SdfLayerHandle& dstLayer, const SdfPath& dstPath, + bool fieldInDst, + boost::optional* valueToCopy + ) { + return SdfShouldCopyValue(topSrcPath, topDstPath, specType, + field, srcLayer, srcPath, fieldInSrc, dstLayer, dstPath, + fieldInDst, valueToCopy); + }; + auto dontCopyChildrenFn = []( + const TfToken& childrenField, const SdfLayerHandle&, const SdfPath&, + bool, const SdfLayerHandle&, const SdfPath&, + bool, boost::optional*, boost::optional* + ) { + // There must be an existing list of static children fields. + // PPT, 18-Oct-21. + static TfToken properties("properties"); + static TfToken primChildren("primChildren"); + + // See traverseLayer() implementation for full list of children field. + // Property children must be copied, prim children must not. What + // about other children field? If we understand the complete list, we + // can encode it in a map, rather than a chain of conditionals. PPT, + // 18-Oct-21. + if (childrenField == properties) { + return true; + } + else if (childrenField == primChildren) { + return false; + } -void UsdMayaPrimUpdater::Clear(UsdMayaPrimUpdaterContext* context) { } + // Default is copy. + return true; + }; -const MDagPath& UsdMayaPrimUpdater::GetDagPath() const { return _dagPath; } + return SdfCopySpec(srcLayer, topSrcPath, dstLayer, topDstPath, + shouldCopyValueFn, dontCopyChildrenFn); +} const MObject& UsdMayaPrimUpdater::GetMayaObject() const { return _mayaObject; } -const SdfPath& UsdMayaPrimUpdater::GetUsdPath() const { return _usdPath; } +const Ufe::Path& UsdMayaPrimUpdater::GetUfePath() const { return _path; } + +UsdPrim UsdMayaPrimUpdater::GetUsdPrim(const UsdMayaPrimUpdaterContext& context) const +{ + return MayaUsd::ufe::ufePathToPrim(_path); +} + +/* static */ +bool UsdMayaPrimUpdater::readPullInformation(const PXR_NS::UsdPrim& prim, std::string& dagPathStr) +{ + auto value = prim.GetCustomDataByKey(kPullPrimMetadataKey); + if (!value.IsEmpty() && value.CanCast()) { + dagPathStr = value.Get(); + return !dagPathStr.empty(); + } + return false; +} + +/* static */ +bool UsdMayaPrimUpdater::readPullInformation( + const PXR_NS::UsdPrim& prim, + Ufe::SceneItem::Ptr& dagPathItem) +{ + std::string dagPathStr; + if (readPullInformation(prim, dagPathStr)) { + // Remove leading '|' character. + if (dagPathStr[0] == '|') + dagPathStr = dagPathStr.substr(1); + dagPathItem = Ufe::Hierarchy::createItem(usdToMaya(dagPathStr)); + return (bool)dagPathItem; + } + return false; +} + +/* static */ +bool UsdMayaPrimUpdater::readPullInformation(const Ufe::Path& ufePath, MDagPath& dagPath) +{ + auto prim = MayaUsd::ufe::ufePathToPrim(ufePath); + std::string dagPathStr; + if (readPullInformation(prim, dagPathStr)) { + MSelectionList sel; + sel.add(dagPathStr.c_str()); + sel.getDagPath(0, dagPath); + return dagPath.isValid(); + } + return false; +} + +/* static */ +bool UsdMayaPrimUpdater::readPullInformation(const MDagPath& dagPath, Ufe::Path& ufePath) +{ + MStatus status; + + MFnDependencyNode depNode(dagPath.node()); + MPlug dgMetadata = depNode.findPlug(kPullDGMetadataKey, &status); + if (status == MStatus::kSuccess) { + MString pulledUfePathStr; + status = dgMetadata.getValue(pulledUfePathStr); + if (status) { + ufePath = Ufe::PathString::path(pulledUfePathStr.asChar()); + return !ufePath.empty(); + } + } + + return false; +} + +/* static */ +bool UsdMayaPrimUpdater::isAnimated(const MDagPath& path) +{ + auto isDagPathAnimated = [](const MDagPath& dagPath) { + int upstreamDependencies = -1; + MString pyCommand; + pyCommand.format( + "import maya.cmds as cmds\n" + "if cmds.evaluationManager( query=True, invalidate=True ):\n" + " upstream = cmds.evaluationManager(ust='^1s')\n" + " for node in upstream:\n" + " if cmds.nodeType(node) == 'mayaUsdProxyShape':\n" + " countNonProxyShape -= 1\n" + " return countNonProxyShape\n" + "else:\n" + " return -1\n", + dagPath.fullPathName().asChar()); + MGlobal::executePythonCommand(pyCommand, upstreamDependencies); + + if (upstreamDependencies >= 0) + return (upstreamDependencies > 0); + else + return MAnimUtil::isAnimated(dagPath, true); + }; + + if (!isDagPathAnimated(path)) { + MItDag dagIt(MItDag::kDepthFirst); + dagIt.reset(path); + for (; !dagIt.isDone(); dagIt.next()) { + MDagPath dagPath; + dagIt.getPath(dagPath); + + if (isDagPathAnimated(dagPath)) + return true; + } + } else { + return true; + } + + return false; +} PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/fileio/primUpdater.h b/lib/mayaUsd/fileio/primUpdater.h index 1f04a0338f..ad2dc53a79 100644 --- a/lib/mayaUsd/fileio/primUpdater.h +++ b/lib/mayaUsd/fileio/primUpdater.h @@ -23,18 +23,22 @@ #include #include +#include #include #include #include +#include +#include + PXR_NAMESPACE_OPEN_SCOPE class UsdMayaPrimUpdater { public: MAYAUSD_CORE_PUBLIC - UsdMayaPrimUpdater(const MFnDependencyNode& depNodeFn, const SdfPath& usdPath); + UsdMayaPrimUpdater(const MFnDependencyNode& depNodeFn, const Ufe::Path& path); // clang errors if you use "= default" here, due to const SdfPath member // see: http://open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#253 @@ -49,24 +53,33 @@ class UsdMayaPrimUpdater Push = 1 << 0, Pull = 1 << 1, Clear = 1 << 2, + AutoPull = 1 << 3, All = Push | Pull | Clear }; + // Copy the pushed prim from the temporary srcLayer where it has been + // exported by push into the destination dstLayer which is in the scene. MAYAUSD_CORE_PUBLIC - virtual bool Push(UsdMayaPrimUpdaterContext* context); - + virtual bool PushCopySpecs( + SdfLayerRefPtr srcLayer, + const SdfPath& srcSdfPath, + SdfLayerRefPtr dstLayer, + const SdfPath& dstSdfPath); + + /// Customize the pulled prim after pull import. Default implementation in + /// this class is a no-op. MAYAUSD_CORE_PUBLIC - virtual bool Pull(UsdMayaPrimUpdaterContext* context); + virtual bool Pull(const UsdMayaPrimUpdaterContext& context); + /// Discard edits done in Maya. Implementation in this class removes the + /// Maya node. MAYAUSD_CORE_PUBLIC - virtual void Clear(UsdMayaPrimUpdaterContext* context); + virtual bool DiscardEdits(const UsdMayaPrimUpdaterContext& context); - /// The source Maya DAG path that we are consuming. - /// - /// If this prim updater is for a Maya DG node and not a DAG node, this will - /// return an invalid MDagPath. + /// Clean up Maya data model at end of push. Implementation in this class + /// calls DiscardEdits(). MAYAUSD_CORE_PUBLIC - const MDagPath& GetDagPath() const; + virtual bool PushEnd(const UsdMayaPrimUpdaterContext& context); /// The MObject for the Maya node being updated by this updater. MAYAUSD_CORE_PUBLIC @@ -74,46 +87,30 @@ class UsdMayaPrimUpdater /// The path of the destination USD prim which we are updating. MAYAUSD_CORE_PUBLIC - const SdfPath& GetUsdPath() const; + const Ufe::Path& GetUfePath() const; /// The destination USD prim which we are updating. - template UsdPrim GetUsdPrim(UsdMayaPrimUpdaterContext& context) const - { - UsdPrim usdPrim; - - if (!TF_VERIFY(GetDagPath().isValid())) { - return usdPrim; - } - - T primSchema = T::Define(context.GetUsdStage(), GetUsdPath()); - if (!TF_VERIFY( - primSchema, - "Could not define given updater type at path '%s'\n", - GetUsdPath().GetText())) { - return usdPrim; - } - usdPrim = primSchema.GetPrim(); - if (!TF_VERIFY( - usdPrim, - "Could not get UsdPrim for given updater type at path '%s'\n", - primSchema.GetPath().GetText())) { - return usdPrim; - } - - return usdPrim; - } + MAYAUSD_CORE_PUBLIC + UsdPrim GetUsdPrim(const UsdMayaPrimUpdaterContext& context) const; -private: - /// The MDagPath for the Maya node being updated, valid only for DAG node - /// prim updaters. - const MDagPath _dagPath; + MAYAUSD_CORE_PUBLIC + static bool readPullInformation(const PXR_NS::UsdPrim& prim, std::string& dagPathStr); + MAYAUSD_CORE_PUBLIC + static bool readPullInformation(const PXR_NS::UsdPrim& prim, Ufe::SceneItem::Ptr& dagPathItem); + MAYAUSD_CORE_PUBLIC + static bool readPullInformation(const Ufe::Path& ufePath, MDagPath& dagPath); + MAYAUSD_CORE_PUBLIC + static bool readPullInformation(const MDagPath& dagpath, Ufe::Path& ufePath); + MAYAUSD_CORE_PUBLIC + static bool isAnimated(const MDagPath& path); +private: /// The MObject for the Maya node being updated, valid for both DAG and DG /// node prim updaters. const MObject _mayaObject; - const SdfPath _usdPath; - const UsdMayaUtil::MDagPathMap _baseDagToUsdPaths; + /// The proxy shape and destination sdf path if provided + const Ufe::Path _path; }; using UsdMayaPrimUpdaterSharedPtr = std::shared_ptr; diff --git a/lib/mayaUsd/fileio/primUpdaterArgs.cpp b/lib/mayaUsd/fileio/primUpdaterArgs.cpp new file mode 100644 index 0000000000..dd10aeb942 --- /dev/null +++ b/lib/mayaUsd/fileio/primUpdaterArgs.cpp @@ -0,0 +1,59 @@ +// +// 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 "primUpdaterArgs.h" + +PXR_NAMESPACE_OPEN_SCOPE + +TF_DEFINE_PUBLIC_TOKENS(UsdMayaPrimUpdaterArgsTokens, PXRUSDMAYA_UPDATER_ARGS_TOKENS); + +/// Extracts a bool at \p key from \p userArgs, or false if it can't extract. +static 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); +} + +UsdMayaPrimUpdaterArgs::UsdMayaPrimUpdaterArgs(const VtDictionary& userArgs) + : _copyOperation(_Boolean(userArgs, UsdMayaPrimUpdaterArgsTokens->copyOperation)) +{ +} + +/*static*/ +UsdMayaPrimUpdaterArgs UsdMayaPrimUpdaterArgs::createFromDictionary(const VtDictionary& userArgs) +{ + return UsdMayaPrimUpdaterArgs( + VtDictionaryOver(userArgs, getDefaultDictionary())); +} + +/*static*/ +const VtDictionary& UsdMayaPrimUpdaterArgs::getDefaultDictionary() +{ + static VtDictionary d; + static std::once_flag once; + std::call_once(once, []() { + d[UsdMayaPrimUpdaterArgsTokens->copyOperation] = false; + }); + + return d; +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/fileio/primUpdaterArgs.h b/lib/mayaUsd/fileio/primUpdaterArgs.h new file mode 100644 index 0000000000..f20d5410c5 --- /dev/null +++ b/lib/mayaUsd/fileio/primUpdaterArgs.h @@ -0,0 +1,59 @@ +// +// 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 PXRUSDMAYA_PRIMUPDATERARGS_H +#define PXRUSDMAYA_PRIMUPDATERARGS_H + +#include + +#include +#include +#include +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + +// clang-format off +#define PXRUSDMAYA_UPDATER_ARGS_TOKENS \ + /* Dictionary keys */ \ + (copyOperation) +// clang-format on + +TF_DECLARE_PUBLIC_TOKENS( + UsdMayaPrimUpdaterArgsTokens, + MAYAUSD_CORE_PUBLIC, + PXRUSDMAYA_UPDATER_ARGS_TOKENS); + +/// \class UsdMayaPrimUpdaterArgs +/// \brief Arguments that configure the updater. +struct UsdMayaPrimUpdaterArgs +{ + const bool _copyOperation{false}; + + MAYAUSD_CORE_PUBLIC + static UsdMayaPrimUpdaterArgs createFromDictionary(const VtDictionary& userArgs); + + MAYAUSD_CORE_PUBLIC + static const VtDictionary& getDefaultDictionary(); + +private: + UsdMayaPrimUpdaterArgs(const VtDictionary& userArgs); + +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif diff --git a/lib/mayaUsd/fileio/primUpdaterContext.cpp b/lib/mayaUsd/fileio/primUpdaterContext.cpp index 58b7134d24..582150fc4b 100644 --- a/lib/mayaUsd/fileio/primUpdaterContext.cpp +++ b/lib/mayaUsd/fileio/primUpdaterContext.cpp @@ -19,13 +19,29 @@ PXR_NAMESPACE_OPEN_SCOPE UsdMayaPrimUpdaterContext::UsdMayaPrimUpdaterContext( - const UsdTimeCode& timeCode, - const UsdStageRefPtr& stage) + const UsdTimeCode& timeCode, + const UsdStageRefPtr& stage, + const VtDictionary& userArgs, + const UsdPathToDagPathMapPtr& pathMap +) : _timeCode(timeCode) , _stage(stage) + , _pathMap(pathMap) + , _userArgs(userArgs) + , _args(UsdMayaPrimUpdaterArgs::createFromDictionary(userArgs)) { } -void UsdMayaPrimUpdaterContext::Clear(const SdfPath&) { } +MDagPath UsdMayaPrimUpdaterContext::MapSdfPathToDagPath( + const SdfPath& sdfPath +) const +{ + if (!_pathMap || sdfPath.IsEmpty()) { + return MDagPath(); + } + + auto found = _pathMap->find(sdfPath); + return found == _pathMap->end() ? MDagPath() : found->second; +} PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/fileio/primUpdaterContext.h b/lib/mayaUsd/fileio/primUpdaterContext.h index 89286e1cda..c2bad47fba 100644 --- a/lib/mayaUsd/fileio/primUpdaterContext.h +++ b/lib/mayaUsd/fileio/primUpdaterContext.h @@ -19,33 +19,56 @@ #include +#include + #include #include #include +#include + +#include + +#include // shared_ptr PXR_NAMESPACE_OPEN_SCOPE /// \class UsdMayaPrimUpdaterContext /// \brief This class provides an interface for updater plugins to communicate /// state back to the core usd maya logic. +// +// Gives access to shared state across prim updaters. class UsdMayaPrimUpdaterContext { public: + + using UsdPathToDagPathMap = TfHashMap; + using UsdPathToDagPathMapPtr = std::shared_ptr; + MAYAUSD_CORE_PUBLIC - UsdMayaPrimUpdaterContext(const UsdTimeCode& timeCode, const UsdStageRefPtr& stage); + UsdMayaPrimUpdaterContext(const UsdTimeCode& timeCode, const UsdStageRefPtr& stage, const VtDictionary& userArgs, const UsdPathToDagPathMapPtr& pathMap = nullptr); /// \brief returns the time frame where data should be edited. const UsdTimeCode& GetTimeCode() const { return _timeCode; } /// \brief returns the usd stage that is being written to. UsdStageRefPtr GetUsdStage() const { return _stage; } + + /// \brief Return dictionary with user defined arguments. Can contain a mix of reader/writer and updater args + const VtDictionary& GetUserArgs() const { return _userArgs; } + + /// \brief Return updater arguments + const UsdMayaPrimUpdaterArgs& GetArgs() const { return _args; } - MAYAUSD_CORE_PUBLIC - virtual void Clear(const SdfPath&); + /// \brief Returns the Maya Dag path corresponding to a pulled USD path. The Dag path will be empty if no correspondence exists. + MDagPath MapSdfPathToDagPath(const SdfPath& sdfPath) const; private: - const UsdTimeCode& _timeCode; - UsdStageRefPtr _stage; + const UsdTimeCode& _timeCode; + const UsdStageRefPtr _stage; + const UsdPathToDagPathMapPtr _pathMap; + + const VtDictionary& _userArgs; + const UsdMayaPrimUpdaterArgs _args; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/fileio/primUpdaterManager.cpp b/lib/mayaUsd/fileio/primUpdaterManager.cpp new file mode 100644 index 0000000000..c27b47d46b --- /dev/null +++ b/lib/mayaUsd/fileio/primUpdaterManager.cpp @@ -0,0 +1,1110 @@ +// +// 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 "primUpdaterManager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +// Allow for use of MObjectHandle with std::unordered_map. +namespace std { +template <> struct hash { + std::size_t operator()(const MObjectHandle& obj) const {return obj.hashCode();} +}; +} + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +//------------------------------------------------------------------------------ +// Global variables +//------------------------------------------------------------------------------ +extern Ufe::Rtid g_MayaRtid; + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF + +namespace { + +const std::string kPullParentPathKey("Maya:Pull:ParentPath"); + +// Set name that will be used to hold all pulled objects +const std::string kPullSetName("pullStateSet"); + +// Metadata key used to store pull information on a prim +const TfToken kPullPrimMetadataKey("Maya:Pull:DagPath"); + +// Metadata key used to store pull information on a DG node +const MString kPullDGMetadataKey("Pull_UfePath"); + +//! Lock or unlock hierarchy starting at given root. +void lockNodes(const MDagPath& root, bool state) +{ + MItDag dagIt; + dagIt.reset(root); + for (; !dagIt.isDone(); dagIt.next()) { + MFnDependencyNode node(dagIt.currentItem()); + if (node.isFromReferencedFile()) { + dagIt.prune(); + continue; + } + node.setLocked(state); + } +} + +Ufe::Path usdToMaya(const Ufe::Path& usdPath) +{ + auto prim = MayaUsd::ufe::ufePathToPrim(usdPath); + if (!TF_VERIFY(prim)) { + return Ufe::Path(); + } + std::string dagPathStr; + if (!TF_VERIFY(PXR_NS::UsdMayaPrimUpdater::readPullInformation( + prim, dagPathStr))) { + return Ufe::Path(); + } + + return Ufe::PathString::path(dagPathStr); +} + +SdfPath ufeToSdfPath(const Ufe::Path& usdPath) +{ + auto segments = usdPath.getSegments(); + + // Can be only a gateway node + if (segments.size() <= 1) { + return {}; + } + + return SdfPath(segments[1].string()); +} + +//------------------------------------------------------------------------------ +// +void select(const MDagPath& dagPath) +{ + MSelectionList selList; + selList.add(dagPath); + MGlobal::setActiveSelectionList(selList, MGlobal::kReplaceList); +} + +//------------------------------------------------------------------------------ +// +// The UFE path and the prim refer to the same object: the prim is passed in as +// an optimization to avoid an additional call to ufePathToPrim(). +bool writePullInformation( + const Ufe::Path& ufePulledPath, + const PXR_NS::UsdPrim& pulledPrim, + const MDagPath& path) +{ + // Add to a set + MObject pullSetObj; + auto status = UsdMayaUtil::GetMObjectByName(kPullSetName, pullSetObj); + if (status != MStatus::kSuccess) { + MFnSet fnSet; + MSelectionList selList; + MObject pullSetObj = fnSet.create(selList, MFnSet::kNone, &status); + fnSet.setName(kPullSetName.c_str()); + fnSet.addMember(path); + } else { + MFnSet fnPullSet(pullSetObj); + fnPullSet.addMember(path); + } + + // Store metadata on the prim. + VtValue value(path.fullPathName().asChar()); + pulledPrim.SetCustomDataByKey(kPullPrimMetadataKey, value); + + // Store medata on DG node + auto ufePathString = Ufe::PathString::string(ufePulledPath); + MFnDependencyNode depNode(path.node()); + MPlug dgMetadata = depNode.findPlug(kPullDGMetadataKey, &status); + if (status != MStatus::kSuccess) { + MFnStringData fnStringData; + MObject strAttrObject = fnStringData.create(""); + + MFnTypedAttribute attr; + MObject attrObj = attr.create(kPullDGMetadataKey, + kPullDGMetadataKey, MFnData::kString, strAttrObject); + status = depNode.addAttribute(attrObj); + dgMetadata = depNode.findPlug(kPullDGMetadataKey, &status); + if (status != MStatus::kSuccess) { + return false; + } + } + dgMetadata.setValue(ufePathString.c_str()); + + return true; +} + +//------------------------------------------------------------------------------ +// +void removePullInformation(const Ufe::Path& ufePulledPath) +{ + UsdPrim prim = MayaUsd::ufe::ufePathToPrim(ufePulledPath); + prim.ClearCustomDataByKey(kPullPrimMetadataKey); +} + +//------------------------------------------------------------------------------ +// +bool addExcludeFromRendering(const Ufe::Path& ufePulledPath) +{ + auto proxyShape = MayaUsd::ufe::getProxyShape(ufePulledPath); + MStatus status; + MFnDependencyNode depNode(proxyShape->thisMObject(), &status); + CHECK_MSTATUS_AND_RETURN(status, false); + + MPlug excludePrimPathsPlug + = depNode.findPlug(MayaUsdProxyShapeBase::excludePrimPathsAttr, &status); + CHECK_MSTATUS_AND_RETURN(status, false); + + MString excludePrimPathsStr; + status = excludePrimPathsPlug.getValue(excludePrimPathsStr); + std::vector excludePrimPaths = TfStringTokenize(excludePrimPathsStr.asChar(), ","); + + std::string sdfPathStr = ufeToSdfPath(ufePulledPath).GetText(); + if (std::find(excludePrimPaths.begin(), excludePrimPaths.end(), sdfPathStr) + != excludePrimPaths.end()) + return true; + + excludePrimPaths.push_back(sdfPathStr); + excludePrimPathsStr = TfStringJoin(excludePrimPaths, ",").c_str(); + excludePrimPathsPlug.setValue(excludePrimPathsStr); + + return true; +} + +//------------------------------------------------------------------------------ +// +bool removeExcludeFromRendering(const Ufe::Path& ufePulledPath) +{ + auto proxyShape = MayaUsd::ufe::getProxyShape(ufePulledPath); + auto prim = MayaUsd::ufe::ufePathToPrim(ufePulledPath); + std::string sdfPathStr = prim.GetPath().GetText(); + + MStatus status; + MFnDependencyNode depNode(proxyShape->thisMObject(), &status); + CHECK_MSTATUS_AND_RETURN(status, false); + + MPlug excludePrimPathsPlug + = depNode.findPlug(MayaUsdProxyShapeBase::excludePrimPathsAttr, &status); + CHECK_MSTATUS_AND_RETURN(status, false); + + MString excludePrimPathsStr; + status = excludePrimPathsPlug.getValue(excludePrimPathsStr); + std::vector excludePrimPaths = TfStringTokenize(excludePrimPathsStr.asChar(), ","); + + auto foundIt = std::find(excludePrimPaths.begin(), excludePrimPaths.end(), sdfPathStr); + if (foundIt == excludePrimPaths.end()) + return true; + + excludePrimPaths.erase(foundIt); + excludePrimPathsStr = TfStringJoin(excludePrimPaths, ",").c_str(); + excludePrimPathsPlug.setValue(excludePrimPathsStr); + return true; +} + +//------------------------------------------------------------------------------ +// +// Perform the import step of the pull (first step), with the argument +// prim as the root of the USD hierarchy to be pulled. The UFE path and +// the prim refer to the same object: the prim is passed in as an +// optimization to avoid an additional call to ufePathToPrim(). +using PullImportPaths = std::pair, std::vector>; +PullImportPaths PullImport( + const Ufe::Path& ufePulledPath, + const UsdPrim& pulledPrim, + const UsdMayaPrimUpdaterContext& context +) +{ + std::vector addedDagPaths; + std::vector pulledUfePaths; + + std::string mFileName = context.GetUsdStage()->GetRootLayer()->GetIdentifier(); + if (mFileName.empty()) { + TF_RUNTIME_ERROR("Empty file specified. Exiting."); + return PullImportPaths(addedDagPaths, pulledUfePaths); + } + + const VtDictionary& userArgs = context.GetUserArgs(); + + UsdMayaJobImportArgs jobArgs = UsdMayaJobImportArgs::CreateFromDictionary( + userArgs, + /* importWithProxyShapes = */ false, + GfInterval::GetFullInterval()); + + MayaUsd::ImportData importData(mFileName); + importData.setRootPrimPath(pulledPrim.GetPath().GetText()); + + UsdMaya_ReadJob readJob(importData, jobArgs); + auto found = userArgs.find("Maya:Pull:ParentPath"); + if (found != userArgs.end()) { + const std::string& dagPathStr = found->second.Get(); + auto pullParent = UsdMayaUtil::nameToDagPath(dagPathStr); + if (pullParent.isValid()) { + readJob.SetMayaRootDagPath(pullParent); + } + } + + // Execute the command + bool success = readJob.Read(&addedDagPaths); + + const bool isCopy = context.GetArgs()._copyOperation; + if (success && !isCopy) { + // Quick workaround to reuse some POC code - to rewrite later + auto ufeChild = MayaUsd::ufe::dagPathToUfe(addedDagPaths[0]); + + // Since we haven't pulled yet, obtaining the parent is simple, and + // doesn't require going through the Hierarchy interface, which can do + // non-trivial work on pulled objects to get their parent. + auto ufeParent = ufePulledPath.pop(); + + MString pyCommand; + // The "child" is the node that will receive the computed parent + // transformation, in its offsetParentMatrix attribute. We are using + // the pull parent for this purpose, so pop the path of the ufeChild to + // get to its pull parent. + pyCommand.format( + "from mayaUsd.lib import proxyAccessor as pa\n" + "import maya.cmds as cmds\n" + "cmds.select('^1s', '^2s')\n" + "pa.parent()\n" + "cmds.select(clear=True)\n", + Ufe::PathString::string(ufeChild.pop()).c_str(), + Ufe::PathString::string(ufeParent).c_str()); + MGlobal::executePythonCommand(pyCommand); + // -- end -- + + // Finalize the pull. + writePullInformation(ufePulledPath, pulledPrim, addedDagPaths[0]); + addExcludeFromRendering(ufePulledPath); + + select(addedDagPaths[0]); + } + + // Invert the new node registry, for MObject to Ufe::Path lookup. + using ObjToUfePath = std::unordered_map; + ObjToUfePath objToUfePath; + const auto& ps = ufePulledPath.getSegments()[0]; + const auto rtid = MayaUsd::ufe::getUsdRunTimeId(); + for (const auto& v : readJob.GetNewNodeRegistry()) { + Ufe::Path::Segments s{ps, Ufe::PathSegment(v.first, rtid, '/')}; + Ufe::Path p(std::move(s)); + objToUfePath.insert(ObjToUfePath::value_type( + MObjectHandle(v.second), p)); + } + + // For each added Dag path, get the UFE path of the pulled USD prim. + pulledUfePaths.reserve(addedDagPaths.size()); + for (const auto& dagPath : addedDagPaths) { + auto found = objToUfePath.find(MObjectHandle(dagPath.node())); + TF_AXIOM(found != objToUfePath.end()); + pulledUfePaths.emplace_back(found->second); + } + + return PullImportPaths(addedDagPaths, pulledUfePaths); +} + +//------------------------------------------------------------------------------ +// +// Perform the customization step of the pull (second step). +bool PullCustomize( + const PullImportPaths& importedPaths, + const UsdMayaPrimUpdaterContext& context +) +{ + TF_AXIOM(importedPaths.first.size() == importedPaths.second.size()); + auto dagPathIt = importedPaths.first.begin(); + auto ufePathIt = importedPaths.second.begin(); + for (; dagPathIt != importedPaths.first.end(); ++dagPathIt, ++ufePathIt) { + const auto& dagPath = *dagPathIt; + const auto& pulledUfePath = *ufePathIt; + MFnDependencyNode dgNodeFn(dagPath.node()); + + const std::string mayaTypeName(dgNodeFn.typeName().asChar()); + + auto registryItem = UsdMayaPrimUpdaterRegistry::FindOrFallback(mayaTypeName); + auto factory = std::get(registryItem); + auto updater = factory(dgNodeFn, pulledUfePath); + + // The failure of a single updater causes failure of the whole + // customization step. This is a frequent difficulty for operations on + // multiple data, especially since we can't roll back the result of + // the execution of previous updaters. Revisit this. PPT, 15-Sep-2021. + if (!updater->Pull(context)) { + return false; + } + } + return true; +} + +//------------------------------------------------------------------------------ +// +// Perform the export step of the merge to USD (first step). Returns the +// source SdfPath and SdfLayer for the next step, push customize. The source +// SdfPath will be empty on error. +using UsdPathToDagPathMap = TfHashMap; +using UsdPathToDagPathMapPtr = std::shared_ptr; +using PushCustomizeSrc = + std::tuple; + +PushCustomizeSrc PushExport( + const Ufe::Path& ufePulledPath, + const MObject& mayaObject, + const UsdMayaPrimUpdaterContext& context +) +{ + UsdStageRefPtr srcStage = UsdStage::CreateInMemory(); + SdfLayerRefPtr srcLayer = srcStage->GetRootLayer(); + UsdPathToDagPathMapPtr pathMapPtr; + auto pushCustomizeSrc = std::make_tuple(SdfPath(), srcLayer, pathMapPtr); + + // Copy to be able to add the export root. + VtDictionary userArgs = context.GetUserArgs(); + + std::string fileName = srcLayer->GetIdentifier(); + + MFnDagNode fnDag(mayaObject); + MDagPath dagPath; + fnDag.getPath(dagPath); + + UsdMayaUtil::MDagPathSet dagPaths; + dagPaths.insert(dagPath); + + GfInterval timeInterval = PXR_NS::UsdMayaPrimUpdater::isAnimated(dagPath) ? + GfInterval(MAnimControl::minTime().value(), MAnimControl::maxTime().value()) : GfInterval(); + double frameStride = 1.0; + std::set frameSamples; + + const std::vector timeSamples + = UsdMayaWriteUtil::GetTimeSamples(timeInterval, frameSamples, frameStride); + + // The pushed Dag node is the root of the export job. + std::vector rootPathString( + 1, VtValue(std::string(dagPath.partialPathName().asChar()))); + userArgs[UsdMayaJobExportArgsTokens->exportRoots] = rootPathString; + + UsdMayaJobExportArgs jobArgs + = UsdMayaJobExportArgs::CreateFromDictionary(userArgs, dagPaths, timeSamples); + + UsdMaya_WriteJob writeJob(jobArgs); + if (!writeJob.Write(fileName, false /* append */)) { + return pushCustomizeSrc; + } + + std::get(pushCustomizeSrc) = writeJob.MapDagPathToSdfPath(dagPath); + + // Invert the Dag path to USD path map, to return it for prim updater use. + auto usdPathToDagPathMap = std::make_shared(); + for (const auto& v : writeJob.GetDagPathToUsdPathMap()) { + usdPathToDagPathMap->insert(UsdPathToDagPathMap::value_type( + v.second, v.first)); + } + + std::get(pushCustomizeSrc) = usdPathToDagPathMap; + + const bool isCopy = context.GetArgs()._copyOperation; + if (!isCopy) { + // FIXME Is it too early to remove the pull information? If we remove + // it here, the push customize step won't have access to it --- but + // maybe it doesn't need to, because the UsdPathToDagPathMap is + // available in the context. PPT, 14-Oct-2021. + removePullInformation(ufePulledPath); + removeExcludeFromRendering(ufePulledPath); + } + + return pushCustomizeSrc; +} + +//------------------------------------------------------------------------------ +// +SdfPath getDstSdfPath( + const Ufe::Path& ufePulledPath, + const SdfPath& srcSdfPath, + bool isCopy +) +{ + // If we got the destination path, extract it, otherwise use src path as + // the destination. + SdfPath dstSdfPath; + if (ufePulledPath.nbSegments() == 2) { + dstSdfPath = SdfPath(ufePulledPath.getSegments()[1].string()); + + if (isCopy) { + SdfPath relativeSrcSdfPath = srcSdfPath.MakeRelativePath(SdfPath::AbsoluteRootPath()); + dstSdfPath = dstSdfPath.AppendPath(relativeSrcSdfPath); + } + } else { + dstSdfPath = srcSdfPath; + } + + return dstSdfPath; +} + +//------------------------------------------------------------------------------ +// +UsdMayaPrimUpdaterSharedPtr createUpdater( + const SdfLayerRefPtr& srcLayer, + const SdfPath& primSpecPath, + const UsdMayaPrimUpdaterContext& context +) +{ + using UpdaterFactoryFn = UsdMayaPrimUpdaterRegistry::UpdaterFactoryFn; + + // Get the primSpec from the src layer. + auto primSpec = srcLayer->GetPrimAtPath(primSpecPath); + if (!TF_VERIFY(primSpec)) { + return nullptr; + } + + TfToken typeName = primSpec->GetTypeName(); + auto regItem = UsdMayaPrimUpdaterRegistry::FindOrFallback(typeName); + auto factory = std::get(regItem); + + // Create the UFE path corresponding to the primSpecPath, as required + // by the prim updater factory. + auto psPath = MayaUsd::ufe::stagePath(context.GetUsdStage()); + Ufe::Path::Segments segments{psPath.getSegments()[0], + MayaUsd::ufe::usdPathToUfePathSegment(primSpecPath)}; + Ufe::Path ufePath(std::move(segments)); + + // Get the Maya object corresponding to the SdfPath. As of 19-Oct-2021, + // the export write job only registers Maya Dag path to SdfPath + // correspondence, so prims that correspond to Maya DG nodes (e.g. material + // networks) don't have a corresponding Dag path. The prim updater + // receives a null MObject in this case. + auto mayaDagPath = context.MapSdfPathToDagPath(primSpecPath); + MFnDependencyNode depNodeFn( + mayaDagPath.isValid() ? mayaDagPath.node() : MObject()); + + return factory(depNodeFn, ufePath); +} + +//------------------------------------------------------------------------------ +// +// Perform the customization step of the merge to USD (second step). Traverse +// the in-memory layer, creating a prim updater for each prim, and call Push +// for each updater. +bool PushCustomize( + const Ufe::Path& ufePulledPath, + const PushCustomizeSrc& src, + const UsdMayaPrimUpdaterContext& context +) + +{ + const auto& srcRootPath = std::get(src); + const auto& srcLayer = std::get(src); + if (srcRootPath.IsEmpty()) { + return false; + } + + const bool isCopy = context.GetArgs()._copyOperation; + const auto& editTarget = context.GetUsdStage()->GetEditTarget(); + auto dstRootPath = editTarget.MapToSpecPath( + getDstSdfPath(ufePulledPath, srcRootPath, isCopy)); + auto dstRootParentPath = dstRootPath.GetParentPath(); + const auto& dstLayer = editTarget.GetLayer(); + + // Traverse the layer, creating a prim updater for each primSpec + // along the way, and call PushCopySpec on the prim. + using UpdaterFactoryFn = UsdMayaPrimUpdaterRegistry::UpdaterFactoryFn; + auto pushCopySpecsFn = [&context, srcLayer, dstLayer, dstRootParentPath]( + const SdfPath& srcPath + ) { + // We can be called with a primSpec path that is not a prim path + // (e.g. a property path like "/A.xformOp:translate"). This is not an + // error, just prune the traversal. FIXME Is this still true? We + // should not be traversing property specs. PPT, 20-Oct-2021. + if (!srcPath.IsPrimPath()) { + return false; + } + + auto updater = createUpdater(srcLayer, srcPath, context); + // If we cannot find an updater for the srcPath, prune the traversal. + if (!updater) { + TF_WARN("Could not create a prim updater for path %s during PushCopySpecs traversal, pruning at that point.", srcPath.GetText()); + return false; + } + auto relativeSrcPath = srcPath.MakeRelativePath(SdfPath::AbsoluteRootPath()); + auto dstPath = dstRootParentPath.AppendPath(relativeSrcPath); + + // Report PushCopySpecs() failure. + if (!updater->PushCopySpecs(srcLayer, srcPath, dstLayer, dstPath)) { + throw MayaUsd::TraversalFailure( + std::string("PushCopySpecs() failed."), srcPath); + } + + // Continue normal traversal without pruning. + return true; + }; + + if (!MayaUsd::traverseLayer(srcLayer, srcRootPath, pushCopySpecsFn)) { + return false; + } + + // Push end is a separate traversal, not a second phase of the same + // traversal, because it is post-order: parents are traversed after + // children. This allows for proper parent lifescope, if push end + // deletes the Maya node (which is the default behavior). + if (isCopy) { + return true; + } + + // SdfLayer::TraversalFn does not return a status, so must report + // failure through an exception. + auto pushEndFn = [&context, srcLayer](const SdfPath& primSpecPath) { + // We can be called with a primSpec path that is not a prim path + // (e.g. a property path like "/A.xformOp:translate"). This is not an + // error, just a no-op. + if (!primSpecPath.IsPrimPath()) { + return; + } + + auto updater = createUpdater(srcLayer, primSpecPath, context); + if (!updater) { + TF_WARN("Could not create a prim updater for path %s during PushEnd() traversal, pruning at that point.", primSpecPath.GetText()); + return; + } + + // Report PushEnd() failure. + if (!updater->PushEnd(context)) { + throw MayaUsd::TraversalFailure( + std::string("PushEnd() failed."), primSpecPath); + } + }; + + // SdfLayer::Traverse does not return a status, so must report failure + // through an exception. + try { + srcLayer->Traverse(srcRootPath, pushEndFn); + } + catch (const MayaUsd::TraversalFailure& e) { + TF_WARN("PushEnd() layer traversal failed for path %s: %s", + e.path().GetAsString(), e.reason().c_str()); + return false; + } + + return true; +} + +class PushPullScope +{ +public: + PushPullScope(bool& controlingFlag) + { + if (!controlingFlag) { + controlingFlag = true; + _controlingFlag = &controlingFlag; + } + } + ~PushPullScope() + { + if (_controlingFlag) { + *_controlingFlag = false; + } + } + +private: + bool* _controlingFlag { nullptr }; +}; + +MString pullRootName("__mayaUsd__"); + +} // namespace + +PXR_NAMESPACE_OPEN_SCOPE + +TF_INSTANTIATE_SINGLETON(PrimUpdaterManager); + +PrimUpdaterManager::PrimUpdaterManager() +{ + MStatus res; + _cbIds.append( + MSceneMessage::addCallback(MSceneMessage::kBeforeNew, beforeNewOrOpenCallback, this, &res)); + CHECK_MSTATUS(res); + _cbIds.append( + MSceneMessage::addCallback(MSceneMessage::kBeforeOpen, beforeNewOrOpenCallback, this, &res)); + CHECK_MSTATUS(res); + + TfSingleton::SetInstanceConstructed(*this); + TfRegistryManager::GetInstance().SubscribeTo(); + + TfWeakPtr me(this); + TfNotice::Register(me, &PrimUpdaterManager::onProxyContentChanged); +} + +PrimUpdaterManager::~PrimUpdaterManager() +{ + MMessage::removeCallbacks(_cbIds); + _cbIds.clear(); +} + +bool PrimUpdaterManager::Push(const MFnDependencyNode& depNodeFn, const Ufe::Path& pulledPath) +{ + MayaUsdProxyShapeBase* proxyShape = MayaUsd::ufe::getProxyShape(pulledPath); + if (!proxyShape) { + return false; + } + + auto pulledPrim = MayaUsd::ufe::ufePathToPrim(pulledPath); + if (!pulledPrim) { + return false; + } + + PushPullScope scopeIt(_inPushPull); + + VtDictionary exportArgs = UsdMayaJobExportArgs::GetDefaultDictionary(); + auto updaterArgs = UsdMayaPrimUpdaterArgs::createFromDictionary(exportArgs); + auto mayaPath = usdToMaya(pulledPath); + MDagPath pullParentPath; + const bool isCopy = updaterArgs._copyOperation; + if (!isCopy) { + // The pull parent is simply the parent of the pulled path. + pullParentPath = MayaUsd::ufe::ufeToDagPath(mayaPath.pop()); + if (!TF_VERIFY(pullParentPath.isValid())) { + return false; + } + lockNodes(pullParentPath, false); + } + + UsdStageRefPtr proxyStage = proxyShape->usdPrim().GetStage(); + UsdMayaPrimUpdaterContext context(proxyShape->getTime(), proxyStage, exportArgs); + + auto ufeMayaItem = Ufe::Hierarchy::createItem(mayaPath); + auto& scene = Ufe::Scene::instance(); + if (!isCopy && TF_VERIFY(ufeMayaItem)) + scene.notify(Ufe::ObjectPreDelete(ufeMayaItem)); + + // The push is done in two stages: + // 1) Perform the export into a temporary layer. + // 2) Traverse the layer and call the prim updater for each prim, for + // per-prim customization. + + // 1) Perform the export to the temporary layer. + auto pushCustomizeSrc = PushExport(pulledPath, depNodeFn.object(), context); + + // 2) Traverse the in-memory layer, creating a prim updater for each prim, + // and call Push for each updater. Build a new context with the USD path + // to Maya path mapping information. + UsdMayaPrimUpdaterContext customizeContext(proxyShape->getTime(), proxyStage, exportArgs, std::get(pushCustomizeSrc)); + + if (!PushCustomize(pulledPath, pushCustomizeSrc, customizeContext)) { + return false; + } + + if (!isCopy) { + if (!TF_VERIFY(RemovePullParent(pullParentPath))) { + return false; + } + } + + auto ufeUsdItem = Ufe::Hierarchy::createItem(pulledPath); + auto hier = Ufe::Hierarchy::hierarchy(ufeUsdItem); + if (TF_VERIFY(hier)) { + scene.notify(Ufe::SubtreeInvalidate(hier->defaultParent())); + } + + return true; +} + +bool PrimUpdaterManager::Pull(const Ufe::Path& path) +{ + MayaUsdProxyShapeBase* proxyShape = MayaUsd::ufe::getProxyShape(path); + if (!proxyShape) { + return false; + } + + auto pulledPrim = MayaUsd::ufe::ufePathToPrim(path); + if (!pulledPrim) { + return false; + } + + PushPullScope scopeIt(_inPushPull); + + VtDictionary importArgs = UsdMayaJobImportArgs::GetDefaultDictionary(); + auto updaterArgs = UsdMayaPrimUpdaterArgs::createFromDictionary(importArgs); + + MDagPath pullParentPath; + if (!updaterArgs._copyOperation && + !(pullParentPath = SetupPullParent(path, importArgs)).isValid()) { + return false; + } + + UsdMayaPrimUpdaterContext context(proxyShape->getTime(), pulledPrim.GetStage(), importArgs); + + auto& scene = Ufe::Scene::instance(); + auto ufeItem = Ufe::Hierarchy::createItem(path); + if (!updaterArgs._copyOperation && TF_VERIFY(ufeItem)) + scene.notify(Ufe::ObjectPreDelete(ufeItem)); + + // The pull is done in two stages: + // 1) Perform the import into Maya. + // 2) Iterate over all imported Dag paths and call the prim updater on + // each, for per-prim customization. + + // 1) Perform the import + PullImportPaths importedPaths = PullImport(path, pulledPrim, context); + + // 2) Iterate over all imported Dag paths. + if (!PullCustomize(importedPaths, context)) { + return false; + } + + if (!updaterArgs._copyOperation) { + // Lock pulled nodes starting at the pull parent. + lockNodes(pullParentPath, true); + } + + // We must recreate the UFE item because it has changed data models (USD -> Maya). + ufeItem = Ufe::Hierarchy::createItem(usdToMaya(path)); + if (TF_VERIFY(ufeItem)) + scene.notify(Ufe::ObjectAdd(ufeItem)); + + return true; +} + +bool PrimUpdaterManager::PullClear(const Ufe::Path& pulledPath) +{ + MayaUsdProxyShapeBase* proxyShape = MayaUsd::ufe::getProxyShape(pulledPath); + if (!proxyShape) { + return false; + } + + PushPullScope scopeIt(_inPushPull); + + auto mayaPath = usdToMaya(pulledPath); + auto mayaDagPath = MayaUsd::ufe::ufeToDagPath(mayaPath); + + VtDictionary userArgs; + UsdMayaPrimUpdaterContext context(proxyShape->getTime(), proxyShape->usdPrim().GetStage(), userArgs); + + auto ufeMayaItem = Ufe::Hierarchy::createItem(mayaPath); + auto& scene = Ufe::Scene::instance(); + if (TF_VERIFY(ufeMayaItem)) + scene.notify(Ufe::ObjectPreDelete(ufeMayaItem)); + + // Unlock the pulled hierarchy, clear the pull information, and remove the + // pull parent, which is simply the parent of the pulled path. + auto pullParent = mayaDagPath; + pullParent.pop(); + if (!TF_VERIFY(pullParent.isValid())) { + return false; + } + lockNodes(pullParent, false); + + MItDag dagIt; + for (dagIt.reset(mayaDagPath); !dagIt.isDone(); dagIt.next()) { + MDagPath curDagPath; + dagIt.getPath(curDagPath); + MFnDependencyNode depNodeFn(curDagPath.node()); + FallbackPrimUpdater fallback(depNodeFn, Ufe::Path()); + fallback.DiscardEdits(context); + } + + removePullInformation(pulledPath); + removeExcludeFromRendering(pulledPath); + + if (!TF_VERIFY(RemovePullParent(pullParent))) { + return false; + } + + auto ufeUsdItem = Ufe::Hierarchy::createItem(pulledPath); + auto hier = Ufe::Hierarchy::hierarchy(ufeUsdItem); + if (TF_VERIFY(hier)) { + scene.notify(Ufe::SubtreeInvalidate(hier->defaultParent())); + } + return true; +} + +bool PrimUpdaterManager::CopyBetween(const Ufe::Path& srcPath, const Ufe::Path& dstPath) +{ + MayaUsdProxyShapeBase* srcProxyShape = MayaUsd::ufe::getProxyShape(srcPath); + MayaUsdProxyShapeBase* dstProxyShape = MayaUsd::ufe::getProxyShape(dstPath); + + PushPullScope scopeIt(_inPushPull); + bool ret = false; + + // Copy from USD to DG + if (srcProxyShape && dstProxyShape == nullptr) { + SdfPath srcSdfPath = ufeToSdfPath(srcPath); + if (srcSdfPath.IsEmpty()) { + return false; + } + + MFnDependencyNode depNodeFn; + + UsdStageRefPtr proxyStage = srcProxyShape->usdPrim().GetStage(); + UsdPrim srcPrim = proxyStage->GetPrimAtPath(srcSdfPath); + TfToken typeName = srcPrim.GetTypeName(); + + auto registryItem = UsdMayaPrimUpdaterRegistry::FindOrFallback(typeName); + auto factory = std::get(registryItem); + UsdMayaPrimUpdaterSharedPtr updater = factory(depNodeFn, srcPath); + + VtDictionary userArgs = UsdMayaJobImportArgs::GetDefaultDictionary(); + // We will only do copy between two data models, setting this in arguments + // to configure the updater + userArgs[UsdMayaPrimUpdaterArgsTokens->copyOperation] = true; + + UsdMayaPrimUpdaterContext context(srcProxyShape->getTime(), proxyStage, userArgs); + + // FIXME Re-implement with proper traversal. PPT, 22-Oct-2021. +#if 0 + ret = updater->Pull(context); +#endif + } + // Copy from DG to USD + else if(srcProxyShape == nullptr && dstProxyShape) { + // Remove the leading "|world| component. + MDagPath dagPath = PXR_NS::UsdMayaUtil::nameToDagPath(srcPath.getSegments()[0].popHead().string()); + MFnDependencyNode dgNodeFn(dagPath.node()); + + const std::string mayaTypeName(dgNodeFn.typeName().asChar()); + + auto registryItem = UsdMayaPrimUpdaterRegistry::FindOrFallback(mayaTypeName); + auto factory = std::get(registryItem); + UsdMayaPrimUpdaterSharedPtr updater = factory(dgNodeFn, dstPath); + + VtDictionary userArgs = UsdMayaJobExportArgs::GetDefaultDictionary(); + + // We will only do copy between two data models, setting this in arguments + // to configure the updater + userArgs[UsdMayaPrimUpdaterArgsTokens->copyOperation] = true; + + UsdMayaPrimUpdaterContext context(dstProxyShape->getTime(), dstProxyShape->usdPrim().GetStage(), userArgs); + + // FIXME Re-implement with proper traversal. PPT, 22-Oct-2021. +#if 0 + ret = updater->Push(context); +#endif + + if(ret) + { + auto ufeItem = Ufe::Hierarchy::createItem(dstPath); + if (TF_VERIFY(ufeItem)) { + Ufe::Scene::instance().notify(Ufe::SubtreeInvalidate(ufeItem)); + } + } + } + // We don't perform copy operations in the same data model in here. + else { + return false; + } + + return ret; +} + +void PrimUpdaterManager::onProxyContentChanged( + const MayaUsdProxyStageObjectsChangedNotice& proxyNotice) +{ + if (_inPushPull) { + return; + } + + const UsdNotice::ObjectsChanged& notice = proxyNotice.GetNotice(); + + Usd_PrimFlagsPredicate predicate = UsdPrimDefaultPredicate; + + auto stage = notice.GetStage(); + for (const auto& changedPath : notice.GetResyncedPaths()) { + if (changedPath == SdfPath::AbsoluteRootPath()) { + continue; + } + + UsdPrim resyncPrim = stage->GetPrimAtPath(changedPath); + UsdPrimRange range(resyncPrim, predicate); + + for (auto it = range.begin(); it != range.end(); it++) { + const UsdPrim& prim = *it; + + TfToken typeName = prim.GetTypeName(); + + auto registryItem = UsdMayaPrimUpdaterRegistry::FindOrFallback(typeName); + auto supports = std::get(registryItem); + + if ((supports & UsdMayaPrimUpdater::Supports::AutoPull) + == UsdMayaPrimUpdater::Supports::AutoPull) { + it.PruneChildren(); + + const Ufe::PathSegment pathSegment + = MayaUsd::ufe::usdPathToUfePathSegment(prim.GetPath()); + const Ufe::Path path = proxyNotice.GetProxyShape().ufePath() + pathSegment; + + Pull(path); + } + } + } +} + +PrimUpdaterManager& PrimUpdaterManager::GetInstance() +{ + return TfSingleton::GetInstance(); +} + +bool PrimUpdaterManager::FindOrCreatePullRoot() +{ + // If we already found the pull root, good to go. + if (!_pullRoot.isNull()) { + return true; + } + + // No saved pull root. Try to find one in the scene, and save its MObject. + auto worldObj = MItDag().root(); + MFnDagNode world(worldObj); + auto nbWorldChildren = world.childCount(); + for (unsigned int i = 0; i < nbWorldChildren; ++i) { + auto childObj = world.child(i); + MFnDependencyNode child(childObj); + if (child.name() == pullRootName) { + _pullRoot = childObj; + return true; + } + } + + // No pull root in the scene, so create one. + MDagModifier dagMod; + MStatus status; + MObject pullRootObj = dagMod.createNode( + MString("transform"), MObject::kNullObj, &status); + if (status != MStatus::kSuccess) { + return false; + } + status = dagMod.renameNode(pullRootObj, pullRootName); + if (status != MStatus::kSuccess) { + return false; + } + + if (dagMod.doIt() != MStatus::kSuccess) { + return false; + } + + // Hide all objects under the pull root in the Outliner so only the pulled + // objects under a proxy shape will be shown. + MFnDependencyNode pullRootFn(pullRootObj); + UsdMayaUtil::SetHiddenInOutliner(pullRootFn, true); + + _pullRoot = pullRootObj; + return true; +} + +MObject PrimUpdaterManager::CreatePullParent(const Ufe::Path& pulledPath) +{ + MDagModifier dagMod; + MStatus status; + MObject pullParentObj = dagMod.createNode( + MString("transform"), _pullRoot, &status); + if (status != MStatus::kSuccess) { + return MObject::kNullObj; + } + + // Rename the pull parent to be the name of the node plus a "Parent" suffix. + status = dagMod.renameNode(pullParentObj, + MString(pulledPath.back().string().c_str()) + MString("Parent")); + + return (dagMod.doIt() == MStatus::kSuccess) ? + pullParentObj : MObject::kNullObj; +} + +bool PrimUpdaterManager::RemovePullParent(const MDagPath& parentDagPath) +{ + if (!TF_VERIFY(parentDagPath.isValid())) { + return false; + } + + MDGModifier dgMod; + if (dgMod.deleteNode(parentDagPath.node()) != MStatus::kSuccess) { + return false; + } + + // If the pull parent was the last child of the pull root, remove the pull + // root as well, and null out our pull root cache. + MFnDagNode pullRoot(_pullRoot); + auto nbPullRootChildren = pullRoot.childCount(); + if (nbPullRootChildren == 1) { + if (dgMod.deleteNode(_pullRoot) != MStatus::kSuccess) { + return false; + } + _pullRoot = MObject::kNullObj; + } + + return dgMod.doIt() == MStatus::kSuccess; +} + +MDagPath PrimUpdaterManager::SetupPullParent( + const Ufe::Path& pulledPath, + VtDictionary& args +) +{ + if (!FindOrCreatePullRoot()) { + return MDagPath(); + } + + auto pullParent = CreatePullParent(pulledPath); + if (pullParent == MObject::kNullObj) { + return MDagPath(); + } + + // Pull parent is not instanced, so use first path found. + MDagPath pullParentPath; + if (MDagPath::getAPathTo(pullParent, pullParentPath) != MStatus::kSuccess) { + return MDagPath(); + } + + // Add pull parent path to import args as a string. + args[kPullParentPathKey] = + VtValue(std::string(pullParentPath.fullPathName().asChar())); + + return pullParentPath; +} + +/*static*/ +void PrimUpdaterManager::beforeNewOrOpenCallback(void* clientData) +{ + auto um = static_cast(clientData); + um->_pullRoot = MObject::kNullObj; +} +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/fileio/primUpdaterManager.h b/lib/mayaUsd/fileio/primUpdaterManager.h new file mode 100644 index 0000000000..e57334b50a --- /dev/null +++ b/lib/mayaUsd/fileio/primUpdaterManager.h @@ -0,0 +1,94 @@ +// +// 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 PXRUSDMAYA_MAYAPRIMUPDATER_MANAGER_H +#define PXRUSDMAYA_MAYAPRIMUPDATER_MANAGER_H + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +class PrimUpdaterManager : public PXR_NS::TfWeakBase +{ +public: + MAYAUSD_CORE_PUBLIC + ~PrimUpdaterManager(); + + MAYAUSD_CORE_PUBLIC + bool Push(const MFnDependencyNode& depNodeFn, const Ufe::Path& pulledPath); + + MAYAUSD_CORE_PUBLIC + bool Pull(const Ufe::Path& path); + + MAYAUSD_CORE_PUBLIC + bool PullClear(const Ufe::Path& path); + + MAYAUSD_CORE_PUBLIC + bool CopyBetween(const Ufe::Path& srcPath, const Ufe::Path& dstPath); + + /// \brief Returns the singleton prim updater manager + MAYAUSD_CORE_PUBLIC + static PrimUpdaterManager& GetInstance(); + +private: + PrimUpdaterManager(); + + void onProxyContentChanged(const MayaUsdProxyStageObjectsChangedNotice& notice); + + //! Ensure the Dag pull root exists. This is the child of the Maya world + //! node under which all pulled nodes are created. + bool FindOrCreatePullRoot(); + + //! Create the pull parent for the pulled hierarchy. This is the node + //! which receives the pulled node's parent transformation. + MObject CreatePullParent(const Ufe::Path& pulledPath); + + //! Remove the pull parent for the pulled hierarchy. + bool RemovePullParent(const MDagPath& pullParent); + + //! Create the pull parent and set it into the prim updater context. + MDagPath SetupPullParent(const Ufe::Path& pulledPath, VtDictionary& args); + + //! Maya scene message callback. + static void beforeNewOrOpenCallback(void* clientData); + + friend class TfSingleton; + + bool _inPushPull { false }; + + // Initialize pull root MObject to null object. + MObject _pullRoot{}; + + // Reset pull root on file new / file open. + MCallbackIdArray _cbIds; +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif diff --git a/lib/mayaUsd/fileio/primUpdaterRegistry.cpp b/lib/mayaUsd/fileio/primUpdaterRegistry.cpp index b5313652e3..2d119ca3ee 100644 --- a/lib/mayaUsd/fileio/primUpdaterRegistry.cpp +++ b/lib/mayaUsd/fileio/primUpdaterRegistry.cpp @@ -17,6 +17,7 @@ #include "primUpdaterRegistry.h" #include +#include #include #include @@ -44,24 +45,36 @@ TF_DEFINE_PRIVATE_TOKENS( ); // clang-format on -typedef std::map _Registry; -static _Registry _reg; +namespace { +typedef std::map _RegistryWithTfType; +static _RegistryWithTfType _regTfType; + +typedef std::map _RegistryWithMayaType; +static _RegistryWithMayaType _regMayaType; +} // namespace /* static */ void UsdMayaPrimUpdaterRegistry::Register( - const TfType& t, + const TfType& tfType, + const std::string& mayaType, UsdMayaPrimUpdater::Supports sup, UsdMayaPrimUpdaterRegistry::UpdaterFactoryFn fn) { - TfToken tfTypeName(t.GetTypeName()); + TfToken tfTypeName(tfType.GetTypeName()); TF_DEBUG(PXRUSDMAYA_REGISTRY) .Msg("Registering UsdMayaPrimWriter for TfType type %s.\n", tfTypeName.GetText()); - std::pair<_Registry::iterator, bool> insertStatus - = _reg.insert(std::make_pair(tfTypeName, std::make_tuple(sup, fn))); + auto insertStatus = _regTfType.insert(std::make_pair(tfTypeName, std::make_tuple(sup, fn))); if (insertStatus.second) { - UsdMaya_RegistryHelper::AddUnloader([tfTypeName]() { _reg.erase(tfTypeName); }); + // register lookup by maya type + _regMayaType.insert(std::make_pair(mayaType, std::make_tuple(sup, fn))); + + // cleanup both registries when tftype gets unloaded + UsdMaya_RegistryHelper::AddUnloader([tfTypeName, mayaType]() { + _regTfType.erase(tfTypeName); + _regMayaType.erase(mayaType); + }); } else { TF_CODING_ERROR("Multiple updaters for TfType %s", tfTypeName.GetText()); } @@ -69,7 +82,7 @@ void UsdMayaPrimUpdaterRegistry::Register( /* static */ UsdMayaPrimUpdaterRegistry::RegisterItem -UsdMayaPrimUpdaterRegistry::Find(const TfToken& usdTypeName) +UsdMayaPrimUpdaterRegistry::FindOrFallback(const TfToken& usdTypeName) { TfRegistryManager::GetInstance().SubscribeTo(); @@ -80,7 +93,7 @@ UsdMayaPrimUpdaterRegistry::Find(const TfToken& usdTypeName) TfToken typeName(typeNameStr); RegisterItem ret; - if (TfMapLookup(_reg, typeName, &ret)) { + if (TfMapLookup(_regTfType, typeName, &ret)) { return ret; } @@ -89,13 +102,59 @@ UsdMayaPrimUpdaterRegistry::Find(const TfToken& usdTypeName) // ideally something just registered itself. if not, we at least put it in // the registry in case we encounter it again. - if (!TfMapLookup(_reg, typeName, &ret)) { + if (!TfMapLookup(_regTfType, typeName, &ret)) { + TF_DEBUG(PXRUSDMAYA_REGISTRY) + .Msg( + "No usdMaya updater plugin for TfType %s. No maya plugin found.\n", + typeName.GetText()); + + // use fallback + ret = std::make_tuple( + UsdMayaPrimUpdater::Supports::All, + [](const MFnDependencyNode& depNodeFn, const Ufe::Path& path) { + return std::make_shared(depNodeFn, path); + }); + + _regTfType[typeName] = ret; + } + + return ret; +} + +/* static */ +UsdMayaPrimUpdaterRegistry::RegisterItem +UsdMayaPrimUpdaterRegistry::FindOrFallback(const std::string& mayaTypeName) +{ + TfRegistryManager::GetInstance().SubscribeTo(); + + RegisterItem ret; + if (TfMapLookup(_regMayaType, mayaTypeName, &ret)) { + return ret; + } else { + // use fallback + ret = std::make_tuple( + UsdMayaPrimUpdater::Supports::All, + [](const MFnDependencyNode& depNodeFn, const Ufe::Path& path) { + return std::make_shared(depNodeFn, path); + }); + + _regMayaType[mayaTypeName] = ret; + } + + /* + static const TfTokenVector SCOPE = { _tokens->UsdMaya, _tokens->PrimUpdater }; + UsdMaya_RegistryHelper::FindAndLoadMayaPlug(SCOPE, mayaTypeName); + + // ideally something just registered itself. if not, we at least put it in + // the registry in case we encounter it again. + if (!TfMapLookup(_regTfType, typeName, &ret)) { TF_DEBUG(PXRUSDMAYA_REGISTRY) .Msg( "No usdMaya updater plugin for TfType %s. No maya plugin found.\n", typeName.GetText()); - _reg[typeName] = {}; + _regTfType[typeName] = {}; } + */ return ret; } diff --git a/lib/mayaUsd/fileio/primUpdaterRegistry.h b/lib/mayaUsd/fileio/primUpdaterRegistry.h index 49d29ad0a5..8d144dc9ca 100644 --- a/lib/mayaUsd/fileio/primUpdaterRegistry.h +++ b/lib/mayaUsd/fileio/primUpdaterRegistry.h @@ -35,10 +35,8 @@ PXR_NAMESPACE_OPEN_SCOPE /// \brief Provides functionality to register and lookup USD updater plugins /// for Maya nodes. /// -/// Use PXRUSDMAYA_REGISTER_UPDATER(mayaTypeName, updaterClass) to register a updater -/// class with the registry. -/// -/// The plugin is expected to update a prim at ctx->GetAuthorPath(). +/// Use PXRUSDMAYA_REGISTER_UPDATER(usdTypeName, mayaTypeName, updaterClass, supports) +/// to register a updater class with the registry. /// /// In order for the core system to discover the plugin, you need a /// \c plugInfo.json that contains the Maya type name and the Maya plugin to @@ -65,12 +63,12 @@ struct UsdMayaPrimUpdaterRegistry /// Updater factory function, i.e. a function that creates a prim updater /// for the given Maya node/USD paths and context. using UpdaterFactoryFn - = std::function; + = std::function; using RegisterItem = std::tuple; /// \brief Register \p fn as a factory function providing a - /// UsdMayaPrimUpdater subclass that can be used to update \p mayaType. + /// UsdMayaPrimUpdater subclass that can be used to update \p tfType \ \p mayaType. /// If you can't provide a valid UsdMayaPrimUpdater for the given arguments, /// return a null pointer from the factory function \p fn. /// @@ -79,7 +77,7 @@ struct UsdMayaPrimUpdaterRegistry /// class MyUpdater : public UsdMayaPrimUpdater { /// static UsdMayaPrimUpdaterSharedPtr Create( /// const MFnDependencyNode& depNodeFn, - /// const SdfPath& usdPath); + /// const Ufe::Path& ufePath); /// }; /// TF_REGISTRY_FUNCTION_WITH_TAG(UsdMayaPrimUpdaterRegistry, MyUpdater) { /// UsdMayaPrimUpdaterRegistry::Register(UsdMayaPrimUpdater::Supports, @@ -87,7 +85,11 @@ struct UsdMayaPrimUpdaterRegistry /// } /// \endcode MAYAUSD_CORE_PUBLIC - static void Register(const TfType& type, UsdMayaPrimUpdater::Supports sup, UpdaterFactoryFn fn); + static void Register( + const TfType& tfType, + const std::string& mayaType, + UsdMayaPrimUpdater::Supports sup, + UpdaterFactoryFn fn); /// \brief Register \p fn as a reader provider for \p T. /// @@ -103,49 +105,57 @@ struct UsdMayaPrimUpdaterRegistry /// } /// \endcode template - static void Register(UsdMayaPrimUpdater::Supports sup, UpdaterFactoryFn fn) + static void + Register(const std::string& mayaType, UsdMayaPrimUpdater::Supports sup, UpdaterFactoryFn fn) { if (TfType t = TfType::Find()) { - Register(t, sup, fn); + Register(t, mayaType, sup, fn); } else { TF_CODING_ERROR("Cannot register unknown TfType: %s.", ArchGetDemangled().c_str()); } } - // takes a usdType (i.e. prim.GetTypeName()) - /// \brief Finds a updater factory if one exists for \p usdTypeName. + /// \brief Finds a updater factory if one exists for \p usdTypeName or returns a fallback + /// updater. /// /// \p usdTypeName should be a usd typeName, for example, /// \code /// prim.GetTypeName() /// \endcode MAYAUSD_CORE_PUBLIC - static RegisterItem Find(const TfToken& usdTypeName); + static RegisterItem FindOrFallback(const TfToken& usdTypeName); + + /// \brief Finds a updater if one exists for \p mayaTypeName or returns a fallback updater. + MAYAUSD_CORE_PUBLIC + static RegisterItem FindOrFallback(const std::string& mayaTypeName); }; /// \brief Registers a pre-existing updater class for the given Maya type; /// the updater class should be a subclass of UsdMayaPrimUpdater with a three-place /// constructor that takes (const MFnDependencyNode& depNodeFn, -/// const SdfPath& usdPath, UsdMayaWriteJobContext& jobCtx) as arguments. +/// const Ufe::Path& path,UsdMayaPrimUpdater::Supports sup) as arguments. /// /// Example: /// \code{.cpp} /// class MyUpdater : public UsdMayaPrimUpdater { /// MyUpdater( /// const MFnDependencyNode& depNodeFn, -/// const SdfPath& usdPath, +/// const Ufe::Path& path, /// UsdMayaPrimUpdater::Supports sup) { /// // ... /// } /// }; -/// PXRUSDMAYA_REGISTER_UPDATER(myUsdTypeName, MyUpdater); +/// PXRUSDMAYA_REGISTER_UPDATER(myUsdTypeName, myMayaTypeName, MyUpdater, +/// (UsdMayaPrimUpdater::Supports::Push | UsdMayaPrimUpdater::Supports::Clear))); /// \endcode -#define PXRUSDMAYA_REGISTER_UPDATER(usdTypeName, updaterClass, supports) \ +#define PXRUSDMAYA_REGISTER_UPDATER(usdTypeName, mayaTypeName, updaterClass, supports) \ TF_REGISTRY_FUNCTION_WITH_TAG(UsdMayaPrimUpdaterRegistry, usdTypeName##_##updaterClass) \ { \ UsdMayaPrimUpdaterRegistry::Register( \ - supports, [](const MFnDependencyNode& depNodeFn, const SdfPath& usdPath) { \ - return std::make_shared(depNodeFn, usdPath); \ + #mayaTypeName, \ + supports, \ + [](const MFnDependencyNode& depNodeFn, const Ufe::Path& path) { \ + return std::make_shared(depNodeFn, path); \ }); \ } diff --git a/lib/mayaUsd/listeners/proxyShapeNotice.cpp b/lib/mayaUsd/listeners/proxyShapeNotice.cpp index 4f3ee6d9cd..6f84c05087 100644 --- a/lib/mayaUsd/listeners/proxyShapeNotice.cpp +++ b/lib/mayaUsd/listeners/proxyShapeNotice.cpp @@ -25,6 +25,7 @@ PXR_NAMESPACE_OPEN_SCOPE TF_INSTANTIATE_TYPE(MayaUsdProxyStageSetNotice, TfType::CONCRETE, TF_1_PARENT(TfNotice)); TF_INSTANTIATE_TYPE(MayaUsdProxyStageInvalidateNotice, TfType::CONCRETE, TF_1_PARENT(TfNotice)); +TF_INSTANTIATE_TYPE(MayaUsdProxyStageObjectsChangedNotice, TfType::CONCRETE, TF_1_PARENT(TfNotice)); MayaUsdProxyStageBaseNotice::MayaUsdProxyStageBaseNotice(const MayaUsdProxyShapeBase& proxy) : _proxy(proxy) @@ -45,4 +46,17 @@ const std::string MayaUsdProxyStageBaseNotice::GetShapePath() const UsdStageRefPtr MayaUsdProxyStageBaseNotice::GetStage() const { return _proxy.getUsdStage(); } +MayaUsdProxyStageObjectsChangedNotice::MayaUsdProxyStageObjectsChangedNotice( + const MayaUsdProxyShapeBase& proxy, + const UsdNotice::ObjectsChanged& notice) + : MayaUsdProxyStageBaseNotice(proxy) + , _notice(notice) +{ +} + +const UsdNotice::ObjectsChanged& MayaUsdProxyStageObjectsChangedNotice::GetNotice() const +{ + return _notice; +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/listeners/proxyShapeNotice.h b/lib/mayaUsd/listeners/proxyShapeNotice.h index bc233aec84..4e0aee6005 100644 --- a/lib/mayaUsd/listeners/proxyShapeNotice.h +++ b/lib/mayaUsd/listeners/proxyShapeNotice.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -62,6 +63,21 @@ class MayaUsdProxyStageInvalidateNotice : public MayaUsdProxyStageBaseNotice using MayaUsdProxyStageBaseNotice::MayaUsdProxyStageBaseNotice; }; +class MayaUsdProxyStageObjectsChangedNotice : public MayaUsdProxyStageBaseNotice +{ +public: + MAYAUSD_CORE_PUBLIC + MayaUsdProxyStageObjectsChangedNotice( + const MayaUsdProxyShapeBase& proxy, + const UsdNotice::ObjectsChanged& notice); + + MAYAUSD_CORE_PUBLIC + const UsdNotice::ObjectsChanged& GetNotice() const; + +private: + const UsdNotice::ObjectsChanged& _notice; +}; + PXR_NAMESPACE_CLOSE_SCOPE #endif diff --git a/lib/mayaUsd/nodes/proxyAccessor.py b/lib/mayaUsd/nodes/proxyAccessor.py index 5804410eaf..22f03d03f7 100644 --- a/lib/mayaUsd/nodes/proxyAccessor.py +++ b/lib/mayaUsd/nodes/proxyAccessor.py @@ -31,6 +31,10 @@ import ufe import re +def isGatewayNode(dagPath): + baseProxyShape = 'mayaUsdProxyShapeBase' + return baseProxyShape in cmds.nodeType(dagPath, inherited=True) + def getUfeSelection(): try: return ufe.GlobalSelection.get().front() @@ -48,7 +52,10 @@ def getDagAndPrimFromUfe(ufeObject): dagPath = str(ufeObject.path().segments[0])[6:] if segmentCount == 1: - return dagPath, None + if isGatewayNode(dagPath): + return dagPath, Sdf.Path.absoluteRootPath + else: + return dagPath, None primPath = str(ufeObject.path().segments[1]) @@ -64,8 +71,12 @@ def getAccessPlugName(sdfPath): def isUfeUsdPath(ufeObject): segmentCount = ufeObject.path().nbSegments() - if segmentCount < 2: + if segmentCount == 0: return False + + if segmentCount == 1: + dagPath = str(ufeObject.path().segments[0])[6:] + return isGatewayNode(dagPath) lastSegment = ufeObject.path().segments[segmentCount-1] return Sdf.Path.IsValidPathString(str(lastSegment)) diff --git a/lib/mayaUsd/nodes/proxyShapeBase.cpp b/lib/mayaUsd/nodes/proxyShapeBase.cpp index 8fe566b035..151a7e2dda 100644 --- a/lib/mayaUsd/nodes/proxyShapeBase.cpp +++ b/lib/mayaUsd/nodes/proxyShapeBase.cpp @@ -1664,6 +1664,7 @@ void MayaUsdProxyShapeBase::_OnStageObjectsChanged(const UsdNotice::ObjectsChang clearBoundingBoxCache(); ProxyAccessor::stageChanged(_usdAccessor, thisMObject(), notice); + MayaUsdProxyStageObjectsChangedNotice(*this, notice).Send(); // Recompute the extents of any UsdGeomBoundable that has authored extents const auto& stage = notice.GetStage(); diff --git a/lib/mayaUsd/python/CMakeLists.txt b/lib/mayaUsd/python/CMakeLists.txt index 464f5fb399..89502b50c0 100644 --- a/lib/mayaUsd/python/CMakeLists.txt +++ b/lib/mayaUsd/python/CMakeLists.txt @@ -50,6 +50,14 @@ target_sources(${PYTHON_TARGET_NAME} wrapShadingMode.cpp ) +# Edit as Maya requires UFE path mapping. +if(CMAKE_UFE_V3_FEATURES_AVAILABLE) + target_sources(${PYTHON_TARGET_NAME} + PRIVATE + wrapPrimUpdaterManager.cpp + ) +endif() + # ----------------------------------------------------------------------------- # compiler configuration # ----------------------------------------------------------------------------- diff --git a/lib/mayaUsd/python/module.cpp b/lib/mayaUsd/python/module.cpp index 4f2ffc2cf1..fc1ced9532 100644 --- a/lib/mayaUsd/python/module.cpp +++ b/lib/mayaUsd/python/module.cpp @@ -16,6 +16,11 @@ #include #include +// UFE v3 used as an indicator of support for edit as Maya. +#if defined(WANT_UFE_BUILD) +#include +#endif + PXR_NAMESPACE_USING_DIRECTIVE TF_WRAP_MODULE @@ -27,6 +32,9 @@ TF_WRAP_MODULE TF_WRAP(ConverterArgs); TF_WRAP(DiagnosticDelegate); TF_WRAP(MeshWriteUtils); +#ifdef UFE_V3_FEATURES_AVAILABLE + TF_WRAP(PrimUpdaterManager); +#endif TF_WRAP(Query); TF_WRAP(ReadUtil); TF_WRAP(RoundTripUtil); diff --git a/lib/mayaUsd/python/wrapPrimUpdaterManager.cpp b/lib/mayaUsd/python/wrapPrimUpdaterManager.cpp new file mode 100644 index 0000000000..ccb30be142 --- /dev/null +++ b/lib/mayaUsd/python/wrapPrimUpdaterManager.cpp @@ -0,0 +1,102 @@ +// +// 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 +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +using namespace std; +using namespace boost::python; +using namespace boost; + +PXR_NAMESPACE_USING_DIRECTIVE + +namespace { +bool push(const std::string& nodeName) +{ + MObject obj; + MStatus status = UsdMayaUtil::GetMObjectByName(nodeName, obj); + if (status != MStatus::kSuccess) + return false; + + MDagPath dagPath; + status = MDagPath::getAPathTo(obj, dagPath); + if (status != MStatus::kSuccess) + return false; + + MFnDagNode dagNode(dagPath, &status); + if (status != MStatus::kSuccess) + return false; + + Ufe::Path path; + if (!UsdMayaPrimUpdater::readPullInformation(dagPath, path)) + return false; + + return PrimUpdaterManager::GetInstance().Push(dagNode, path); +} + +bool pull(const std::string& ufePathString) +{ + Ufe::Path path = Ufe::PathString::path(ufePathString); + + return PrimUpdaterManager::GetInstance().Pull(path); +} + +bool pullClear(const std::string& nodeName) +{ + auto dagPath = UsdMayaUtil::nameToDagPath(nodeName); + + Ufe::Path path; + if (!UsdMayaPrimUpdater::readPullInformation(dagPath, path)) + return false; + + return PrimUpdaterManager::GetInstance().PullClear(path); +} + +bool copyBetween(const std::string& srcUfePathString, const std::string& dstUfePathString) +{ + Ufe::Path src = Ufe::PathString::path(srcUfePathString); + Ufe::Path dst = Ufe::PathString::path(dstUfePathString); + + if(src.empty() || dst.empty()) + return false; + + return PrimUpdaterManager::GetInstance().CopyBetween(src, dst); +} + +} // namespace + +void wrapPrimUpdaterManager() +{ + using This = PrimUpdaterManager; + class_("PrimUpdaterManager", no_init) + .def("push", push) + .def("pull", pull) + .def("pullClear", pullClear) + .def("mergeToUsd", push) + .def("editAsMaya", pull) + .def("discardEdits", pullClear) + .def("copy", copyBetween); +} diff --git a/lib/mayaUsd/resources/icons/CMakeLists.txt b/lib/mayaUsd/resources/icons/CMakeLists.txt index b9d75db34d..6d601f6d30 100644 --- a/lib/mayaUsd/resources/icons/CMakeLists.txt +++ b/lib/mayaUsd/resources/icons/CMakeLists.txt @@ -50,6 +50,9 @@ set(LIB_ICONS saveOption1 saveOption2 saveOption3 + discard_edits + edit_as_Maya + merge_to_USD ) foreach(ICON_BASE ${LIB_ICONS}) # The _100.png files need to be installed without the _100. This is the diff --git a/lib/mayaUsd/resources/icons/discard_edits_100.png b/lib/mayaUsd/resources/icons/discard_edits_100.png new file mode 100644 index 0000000000000000000000000000000000000000..9055f17f359dd9c9ec8adb0d9be86e9c08ffaddd GIT binary patch literal 393 zcmV;40e1e0P)Y<{RVE;{)huc$B8lI#pB*EnX`*}VKzG`M zdhk5&$e)bYxoMhD&G)GI`i%kxJe#K3M^W@)7)H?h7%CP^~)0hzI&1d5<+X)J9@ zV8;^!I+YCBdJ~sYUbvJ_SC9oka6?ATSlF*h!Z4&|ptcHlNIJ|xRaG|)TN{f6bzRdi n4Bra$XZ-Uhi+}$czXTWnwXwF&Be?_m00000NkvXXu0mjfad)E? literal 0 HcmV?d00001 diff --git a/lib/mayaUsd/resources/icons/discard_edits_150.png b/lib/mayaUsd/resources/icons/discard_edits_150.png new file mode 100644 index 0000000000000000000000000000000000000000..1836c9112beddaf304e336df9b07b94825be226f GIT binary patch literal 454 zcmV;%0XhDOP)0B-^lD@=@0 z!~;|y1an(u41JEKW2Ck#EW7}%(vc0G0Nw-;y+ahGeA!JKRh4S#1lZ@_-#+^e+B6Nh zWLuF7x4$nSPIVfFv4+PM>5*!pH! zhW*b>0f=Fo=lMezhI1k#IQrro7>kEII+bW}4(%!xMcE-7L1kICAZ#;mzgr-;z@!MJ zw1)>lK#HR1;Zp*vJn)C%T7sXQXy2(I1qTxIC;TZayD=>m%SU$qVE3*0u9AkcZ zCOjfjrNTUg;tVYw2xIDs48i)OPaf7V%BLT9Y+I-B`?E^T|t{27n0tO*FYV_mS`isu*B zN7h#Z@d1BD%KR;lJur!H+Z5C#c%#vvAPC5D91_k4CUDHdeAo%{3w~F(jX+C+ce`D3 zT{qG3cuZV7UI z{Mit|2BPV7N^u;ggBlVi;1JkNCX*w7HUzNSM<~?@yNQGjT%-$+Bi@8>WllkoYjzUC znFLqAFD_Ud<6su6QXtM4F?RMJ;ZaSi{gMHav@PUu@dYy#X0IsbQu!Fto&!!ZkdF;5A zR%BO1W6A)}EZY*C?J>O5HKV*zj2qDxYejjL1)g5X#D)u%;VrB7MwHi#V016?_mp=h s6eO_gKa1W8o$EiND}jGc;75P~06k1p$NvrbKL7v#07*qoM6N<$f&(oKssI20 literal 0 HcmV?d00001 diff --git a/lib/mayaUsd/resources/icons/edit_as_Maya_100.png b/lib/mayaUsd/resources/icons/edit_as_Maya_100.png new file mode 100644 index 0000000000000000000000000000000000000000..232e001f2ae20198d706c3df7d1c625c7a0e2166 GIT binary patch literal 425 zcmV;a0apHrP)^Y0G;|geFEP=>X1%_7D5*@7@{I{a%v@eC@zgXH#9|zO>O(b&%q<# ze-8I5Q`a?6IgSH8B%(`nwQCUQuM_kW`WDUoHsF*H^5}Wqk*cbbW(eB{W*8>6ZF^u@ z78r&>dy_OxL#!U@G5KLQ91h|*hCI&!pFxr&`c6X&>`yRVn@$x)5rQCqq9|ZKpVNaJ zqYJhVOeM?m7LKCm4R4OZ&)*&59!#}ZEVh#oHg|AWz$H3Y6lD(%!?4_JHsdDT6>#mk zE-A|rX0urne(i$W*LckH{P}1!0$ta^_kCEcR*+>Gh0nU+_6Ap)rV*B9f#WzZO%swN zSs#Mi8(av20MqFdmdoXPRaN)cyEz0?F^!)|#^R&zKI==LZ~QCZuOjeIeF!iB1HFYn T1p}cE00000NkvXXu0mjf5tyv< literal 0 HcmV?d00001 diff --git a/lib/mayaUsd/resources/icons/edit_as_Maya_150.png b/lib/mayaUsd/resources/icons/edit_as_Maya_150.png new file mode 100644 index 0000000000000000000000000000000000000000..9b5f4439d87e6c1933493d08ed3b5a01eb1bfe48 GIT binary patch literal 545 zcmV++0^a?JP)Fu+8wmmSfLsKomtc908@GmeSE~w>Pqa9O?sgi()pNq8!ij zPsL)93kHMNMkbzr!k-I;0w0M)09!CJS=S)Pvg~U*ou--V9cX4~;#qg**=&|=G#a3) z>SBOQGMNlLUZ9RFPmxVp^qKp7KB!i!K(!xzc2X^q^AQOVu>-BGpCAjGxtQ46koPyHQY&NF|KUsx4+2kA4TQZq6 zg_q0aKWTAlK*#J-k|d_n=|H#Jbq%-MgX2ITa2SurAruOMEXy#T&momcIfmOATqP0- z{lV||L#b5KO-7>;^!t5BaH|v@a2$6Ui^UclUauGO`8+6!qB(_It>7Ht`xtr=4F&_~ z^?FpbUl?~z;Z_D`;cysgwVDo|OeXX@en35wp`9j^NfLrlXymWSfx%z^m&>(vaSy~hB>}i;2Zmu_G#WuFm4fMXx&sl7 zM!D1bN&#%Gc`OzKwOS3GPG<*VFc?6gP~c9#C;_l#PS)JvaO{O3=7~fChQlHG-&fRg z{*6@o_5z7Qv}4YXMxz0_Tn?7YC78`-n9XK%X;^bo4<`IV#K;}_pJBJ#H#5=gc6ZF> zJcg3yTv#j?&}y~Z>31RXy#NB!f3P{X=4=v@xxOc4e#l;M3-uA@AdlLi=DdVE6*E5s z-~%zIQ-#ktnOdRf_xq4cCNE?zl>=_nZM|L(Znv9ebvm7fcDt>ZxfH+|r9g91#XKGl zEEWsMWHQidwG=TI12CW-lXdrcy+GPXBtnk~L^hj+X0s{VTr3A3kzLDXv(aKgD;7e8 z!(rN1DisQ$)9GL`nf$f67{F8VLq@z+Hqks33emP)F3UC-0&t@4lH?;*jUV&*9P0Ht z^m@I`>zy2PApkmhr_%`AR5SD4HVZU z)&-f~-rfvKN=jf3hz9YY-oXt(fe6A@R#rMdJPnBd@7=riKZu6%bzy3-VYmx`0d@t5 zvtWEhMMV(x7$|m`=s-jU!NI`{LPA1qz%XbAg#!?ClVrgD{refx)6*H8oSbUJ#lkv9)^gB2!^VvDy#v@g3o{N-o4Y`v}qGalM^UdK#7Ya z1AqZ|iBL2$F;EDw88E%Jww3{#bFl$NN^>K{UH}L%0O?0*f>l#6NB{r;07*qoM6N<$ Ef{HDQJOBUy literal 0 HcmV?d00001 diff --git a/lib/mayaUsd/resources/icons/merge_to_USD_150.png b/lib/mayaUsd/resources/icons/merge_to_USD_150.png new file mode 100644 index 0000000000000000000000000000000000000000..e5415f5d86c7e3bb92df63427daa507d3ca69ea0 GIT binary patch literal 542 zcmV+(0^$9MP)kH<;di5$FDE0t}ld2vKU1l({C0I?$@ zBN>2li$H29iR4TlA0GxeIXMi4z#wA)$$`{R5=4ej15!c!=H_Owcx`PhOv!8@o({z4 z$qgcq#x#({$bzh_tRTu4DtDgh84VOCEG#UWfy^M95N0ANjntL6nm4*&oF07*qoM6N<$g8lr^`Tzg` literal 0 HcmV?d00001 diff --git a/lib/mayaUsd/resources/icons/merge_to_USD_200.png b/lib/mayaUsd/resources/icons/merge_to_USD_200.png new file mode 100644 index 0000000000000000000000000000000000000000..c7047d7dc22a2087a39125ad4ea64187eb417d54 GIT binary patch literal 642 zcmV-|0)737P)QWH8R2qkvDU^Khd9U|f-Z}4l=bdw(v0ANgNH}m{9NdAUZ8xUV>0=r)^0(LT_kZbdz}O}S z#P~O&UzUCo<2sz!R)PyQp3mpO6h|)hk^}VgXEdDpd_JU7DMi0u1>#h$*(ZTD^!0ij zUawcJPp4C~TCF=Nh)+sEQtcdpyU?$VGkC5`3dg@f&*A2<--lHS;+Y*eaKb;8N(F{t zJk*ZX_?mUE?mBQod@ounG@DH|HX4n`_UerQCC)hwU%2Qz_Vj{V*>i5oYsgLzAA~m& ziL9sJpJROIUXaV>;B-29`iXeCcLzB83x&d3iqFI@F|;=a7K;TEi3G~!GF&bfFMTBb zBwk5WOfDC;IneEPF`La`nkH(s8e*{+91eA>55$h-#vdY=*&C`UZ=T(btr(BTXt&!a zl}ZSQ!zdPun9t`J4u=?xMwm<{s$4IZOGpd9+*B)$c1<2O(CKs#kH?YCW)TPkkW3~O zt&p0L(Hem@xRDJ81I1u4h-fs5P$&fMoF0z{Znsj7P c4*V5h0QmBukb^R1I{*Lx07*qoM6N<$f&u0r0{{R3 literal 0 HcmV?d00001 diff --git a/lib/mayaUsd/ufe/CMakeLists.txt b/lib/mayaUsd/ufe/CMakeLists.txt index cdbe47cecc..659d394784 100644 --- a/lib/mayaUsd/ufe/CMakeLists.txt +++ b/lib/mayaUsd/ufe/CMakeLists.txt @@ -71,6 +71,9 @@ endif() if(CMAKE_UFE_V3_FEATURES_AVAILABLE) target_sources(${PROJECT_NAME} PRIVATE + PulledObjectHierarchy.cpp + PulledObjectHierarchyHandler.cpp + UsdPathMappingHandler.cpp UsdUndoUngroupCommand.cpp ) endif() @@ -144,6 +147,9 @@ endif() if(CMAKE_UFE_V3_FEATURES_AVAILABLE) list(APPEND HEADERS + PulledObjectHierarchy.h + PulledObjectHierarchyHandler.h + UsdPathMappingHandler.h UsdUndoUngroupCommand.h ) endif() diff --git a/lib/mayaUsd/ufe/Global.cpp b/lib/mayaUsd/ufe/Global.cpp index 94786b765e..f8af937a3a 100644 --- a/lib/mayaUsd/ufe/Global.cpp +++ b/lib/mayaUsd/ufe/Global.cpp @@ -38,6 +38,11 @@ #include #include #endif +#ifdef UFE_V3_FEATURES_AVAILABLE +#define HAVE_PATH_MAPPING +#include +#include +#endif #include #include @@ -108,7 +113,12 @@ MStatus initialize() g_MayaHierarchyHandler = Ufe::RunTimeMgr::instance().hierarchyHandler(g_MayaRtid); auto proxyShapeHierHandler = ProxyShapeHierarchyHandler::create(g_MayaHierarchyHandler); +#ifdef UFE_V3_FEATURES_AVAILABLE + auto pulledObjectHierHandler = PulledObjectHierarchyHandler::create(proxyShapeHierHandler); + Ufe::RunTimeMgr::instance().setHierarchyHandler(g_MayaRtid, pulledObjectHierHandler); +#else Ufe::RunTimeMgr::instance().setHierarchyHandler(g_MayaRtid, proxyShapeHierHandler); +#endif #ifdef UFE_V2_FEATURES_AVAILABLE g_MayaContextOpsHandler = Ufe::RunTimeMgr::instance().contextOpsHandler(g_MayaRtid); @@ -152,6 +162,12 @@ MStatus initialize() g_USDRtid = Ufe::RunTimeMgr::instance().register_(kUSDRunTimeName, handlers); MayaUsd::ufe::UsdUIUfeObserver::create(); + +#ifdef HAVE_PATH_MAPPING + auto pathMappingHndlr = UsdPathMappingHandler::create(); + Ufe::RunTimeMgr::instance().setPathMappingHandler(g_MayaRtid, pathMappingHndlr); +#endif + #else auto usdHierHandler = UsdHierarchyHandler::create(); auto usdTrans3dHandler = UsdTransform3dHandler::create(); @@ -193,6 +209,11 @@ MStatus finalize() Ufe::RunTimeMgr::instance().unregister(g_USDRtid); g_MayaHierarchyHandler.reset(); +#ifdef HAVE_PATH_MAPPING + // Remove the Maya path mapping handler that we added above. + Ufe::RunTimeMgr::instance().setPathMappingHandler(g_MayaRtid, nullptr); +#endif + g_StagesSubject.Reset(); return MS::kSuccess; diff --git a/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp b/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp index 438ba9ef81..8d103c02f9 100644 --- a/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp +++ b/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp @@ -34,6 +34,11 @@ #include #endif +#ifdef UFE_V3_FEATURES_AVAILABLE +#include +#include // In UFE v2 but only needed for primUpdater. +#endif + PXR_NAMESPACE_USING_DIRECTIVE namespace { @@ -170,11 +175,24 @@ Ufe::SceneItemList ProxyShapeHierarchy::createUFEChildList(const UsdPrimSiblingR // single component appended to it. auto parentPath = fItem->path(); Ufe::SceneItemList children; + UFE_V3(std::string dagPathStr;) for (const auto& child : range) { - children.emplace_back(UsdSceneItem::create( - parentPath - + Ufe::PathSegment(Ufe::PathComponent(child.GetName().GetString()), g_USDRtid, '/'), - child)); +#ifdef UFE_V3_FEATURES_AVAILABLE + if (PXR_NS::UsdMayaPrimUpdater::readPullInformation(child, dagPathStr)) { + auto item = Ufe::Hierarchy::createItem(Ufe::PathString::path(dagPathStr)); + if (TF_VERIFY(item, "No item for pulled path '%s'\n", dagPathStr.c_str())) { + children.emplace_back(item); + } + } else { +#endif + children.emplace_back(UsdSceneItem::create( + parentPath + + Ufe::PathSegment( + Ufe::PathComponent(child.GetName().GetString()), g_USDRtid, '/'), + child)); +#ifdef UFE_V3_FEATURES_AVAILABLE + } +#endif } return children; } diff --git a/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp b/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp new file mode 100644 index 0000000000..7a44de0302 --- /dev/null +++ b/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp @@ -0,0 +1,133 @@ +// +// Copyright 2019 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 "PulledObjectHierarchy.h" + +#include + +// Needed because of TF_CODING_ERROR +PXR_NAMESPACE_USING_DIRECTIVE + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +//------------------------------------------------------------------------------ +// PulledObjectHierarchy +//------------------------------------------------------------------------------ + +PulledObjectHierarchy::PulledObjectHierarchy( + const Ufe::HierarchyHandler::Ptr& mayaHierarchyHandler, + const Ufe::SceneItem::Ptr& item, + const Ufe::Path& pulledPath) + : Ufe::Hierarchy() + , _mayaHierarchyHandler(mayaHierarchyHandler) + , _mayaHierarchy(_mayaHierarchyHandler->hierarchy(item)) + , _pulledPath(pulledPath) +{ +} + +/*static*/ +PulledObjectHierarchy::Ptr PulledObjectHierarchy::create( + const Ufe::HierarchyHandler::Ptr& mayaHierarchyHandler, + const Ufe::SceneItem::Ptr& item, + const Ufe::Path& pulledPath) +{ + return std::make_shared(mayaHierarchyHandler, item, pulledPath); +} + +//------------------------------------------------------------------------------ +// Ufe::Hierarchy overrides +//------------------------------------------------------------------------------ + +Ufe::SceneItem::Ptr PulledObjectHierarchy::sceneItem() const { return _mayaHierarchy->sceneItem(); } + +bool PulledObjectHierarchy::hasChildren() const { return _mayaHierarchy->hasChildren(); } + +Ufe::SceneItemList PulledObjectHierarchy::children() const { return _mayaHierarchy->children(); } + +Ufe::SceneItemList PulledObjectHierarchy::filteredChildren(const ChildFilter& childFilter) const +{ + return _mayaHierarchy->filteredChildren(childFilter); +} + +Ufe::SceneItem::Ptr PulledObjectHierarchy::parent() const +{ + return Ufe::Hierarchy::createItem(_pulledPath.pop()); +} + +Ufe::InsertChildCommand::Ptr PulledObjectHierarchy::insertChildCmd( + const Ufe::SceneItem::Ptr& child, + const Ufe::SceneItem::Ptr& pos) +{ + TF_CODING_ERROR("Illegal call to unimplemented %s", __func__); + return nullptr; +} + +Ufe::SceneItem::Ptr +PulledObjectHierarchy::insertChild(const Ufe::SceneItem::Ptr& child, const Ufe::SceneItem::Ptr& pos) +{ + TF_CODING_ERROR("Illegal call to unimplemented %s", __func__); + return nullptr; +} + +Ufe::SceneItem::Ptr PulledObjectHierarchy::createGroup( +#if (UFE_PREVIEW_VERSION_NUM < 3005) + const Ufe::Selection& selection, +#endif + const Ufe::PathComponent& name) const +{ + TF_CODING_ERROR("Illegal call to unimplemented %s", __func__); + return nullptr; +} + +#if (UFE_PREVIEW_VERSION_NUM >= 3001) +Ufe::InsertChildCommand::Ptr +#else +Ufe::UndoableCommand::Ptr +#endif +PulledObjectHierarchy::createGroupCmd( +#if (UFE_PREVIEW_VERSION_NUM < 3005) + const Ufe::Selection& selection, +#endif + const Ufe::PathComponent& name) const +{ + TF_CODING_ERROR("Illegal call to unimplemented %s", __func__); + return nullptr; +} + +Ufe::UndoableCommand::Ptr +PulledObjectHierarchy::reorderCmd(const Ufe::SceneItemList& orderedList) const +{ + TF_CODING_ERROR("Illegal call to unimplemented %s", __func__); + return nullptr; +} + +#ifdef UFE_V3_FEATURES_AVAILABLE +Ufe::UndoableCommand::Ptr PulledObjectHierarchy::ungroupCmd() const +{ + TF_CODING_ERROR("Illegal call to unimplemented %s", __func__); + return nullptr; +} +#endif // UFE_V3_FEATURES_AVAILABLE + +Ufe::SceneItem::Ptr PulledObjectHierarchy::defaultParent() const +{ + // Pulled objects cannot be unparented. + TF_CODING_ERROR("Illegal call to unimplemented %s", __func__); + return nullptr; +} + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/PulledObjectHierarchy.h b/lib/mayaUsd/ufe/PulledObjectHierarchy.h new file mode 100644 index 0000000000..70368b6854 --- /dev/null +++ b/lib/mayaUsd/ufe/PulledObjectHierarchy.h @@ -0,0 +1,95 @@ +// +// Copyright 2019 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. +// +#pragma once + +#include + +#include +#include + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +//! \brief TODOC +/*! + TODOC + */ +class MAYAUSD_CORE_PUBLIC PulledObjectHierarchy : public Ufe::Hierarchy +{ +public: + typedef std::shared_ptr Ptr; + + PulledObjectHierarchy( + const Ufe::HierarchyHandler::Ptr& mayaHierarchyHandler, + const Ufe::SceneItem::Ptr& item, + const Ufe::Path& pulledPath); + + // Delete the copy/move constructors assignment operators. + PulledObjectHierarchy(const PulledObjectHierarchy&) = delete; + PulledObjectHierarchy& operator=(const PulledObjectHierarchy&) = delete; + PulledObjectHierarchy(PulledObjectHierarchy&&) = delete; + PulledObjectHierarchy& operator=(PulledObjectHierarchy&&) = delete; + + //! Create a PulledObjectHierarchy from a UFE hierarchy handler. + static PulledObjectHierarchy::Ptr create( + const Ufe::HierarchyHandler::Ptr& mayaHierarchyHandler, + const Ufe::SceneItem::Ptr& item, + const Ufe::Path& pulledPath); + + // Ufe::Hierarchy overrides + Ufe::SceneItem::Ptr sceneItem() const override; + bool hasChildren() const override; + Ufe::SceneItemList children() const override; + Ufe::SceneItemList filteredChildren(const ChildFilter&) const override; + Ufe::SceneItem::Ptr parent() const override; + + Ufe::SceneItem::Ptr createGroup( +#if (UFE_PREVIEW_VERSION_NUM < 3005) + const Ufe::Selection& selection, +#endif + const Ufe::PathComponent& name) const override; +#if (UFE_PREVIEW_VERSION_NUM >= 3001) + Ufe::InsertChildCommand::Ptr +#else + Ufe::UndoableCommand::Ptr +#endif + createGroupCmd( +#if (UFE_PREVIEW_VERSION_NUM < 3005) + const Ufe::Selection& selection, +#endif + const Ufe::PathComponent& name) const override; + + Ufe::SceneItem::Ptr defaultParent() const override; + + Ufe::SceneItem::Ptr + insertChild(const Ufe::SceneItem::Ptr& child, const Ufe::SceneItem::Ptr& pos) override; + Ufe::InsertChildCommand::Ptr + insertChildCmd(const Ufe::SceneItem::Ptr& child, const Ufe::SceneItem::Ptr& pos) override; + + Ufe::UndoableCommand::Ptr reorderCmd(const Ufe::SceneItemList& orderedList) const override; + +#ifdef UFE_V3_FEATURES_AVAILABLE + Ufe::UndoableCommand::Ptr ungroupCmd() const override; +#endif + +private: + Ufe::HierarchyHandler::Ptr _mayaHierarchyHandler; + Hierarchy::Ptr _mayaHierarchy; + Ufe::Path _pulledPath; +}; // PulledObjectHierarchy + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/PulledObjectHierarchyHandler.cpp b/lib/mayaUsd/ufe/PulledObjectHierarchyHandler.cpp new file mode 100644 index 0000000000..e600ad347a --- /dev/null +++ b/lib/mayaUsd/ufe/PulledObjectHierarchyHandler.cpp @@ -0,0 +1,75 @@ +// +// Copyright 2019 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 "PulledObjectHierarchyHandler.h" + +#include +#include +#include + +#include +#include + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +PulledObjectHierarchyHandler::PulledObjectHierarchyHandler( + const Ufe::HierarchyHandler::Ptr& mayaHierarchyHandler) + : Ufe::HierarchyHandler() + , _mayaHierarchyHandler(mayaHierarchyHandler) +{ +} + +/*static*/ +PulledObjectHierarchyHandler::Ptr +PulledObjectHierarchyHandler::create(const Ufe::HierarchyHandler::Ptr& mayaHierarchyHandler) +{ + return std::make_shared(mayaHierarchyHandler); +} + +//------------------------------------------------------------------------------ +// Ufe::HierarchyHandler overrides +//------------------------------------------------------------------------------ + +Ufe::Hierarchy::Ptr PulledObjectHierarchyHandler::hierarchy(const Ufe::SceneItem::Ptr& item) const +{ + if (!item || item->path().empty()) + return nullptr; + + // Remove the "world" (but account for an item which is just "world"). + const auto& path = item->path().popHead(); + if (path.empty()) + return _mayaHierarchyHandler->hierarchy(item); + + auto nbSegs = path.nbSegments(); + Ufe::Path ufePath; + + auto dagPath = PXR_NS::UsdMayaUtil::nameToDagPath(path.getSegments()[nbSegs - 1].string()); + if (PXR_NS::UsdMayaPrimUpdater::readPullInformation(dagPath, ufePath)) { + return PulledObjectHierarchy::create(_mayaHierarchyHandler, item, ufePath); + } else { + return _mayaHierarchyHandler->hierarchy(item); + } +} + +Ufe::SceneItem::Ptr PulledObjectHierarchyHandler::createItem(const Ufe::Path& path) const +{ + return _mayaHierarchyHandler->createItem(path); +} + +Ufe::Hierarchy::ChildFilter PulledObjectHierarchyHandler::childFilter() const { return {}; } + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/PulledObjectHierarchyHandler.h b/lib/mayaUsd/ufe/PulledObjectHierarchyHandler.h new file mode 100644 index 0000000000..d3b838b762 --- /dev/null +++ b/lib/mayaUsd/ufe/PulledObjectHierarchyHandler.h @@ -0,0 +1,57 @@ +// +// Copyright 2019 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. +// +#pragma once + +#include + +#include + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +//! \brief Maya run-time hierarchy handler with support for pulled Maya objects. +/*! + TO DOC + */ +class MAYAUSD_CORE_PUBLIC PulledObjectHierarchyHandler : public Ufe::HierarchyHandler +{ +public: + typedef std::shared_ptr Ptr; + + PulledObjectHierarchyHandler(const Ufe::HierarchyHandler::Ptr& mayaHierarchyHandler); + + // Delete the copy/move constructors assignment operators. + PulledObjectHierarchyHandler(const PulledObjectHierarchyHandler&) = delete; + PulledObjectHierarchyHandler& operator=(const PulledObjectHierarchyHandler&) = delete; + PulledObjectHierarchyHandler(PulledObjectHierarchyHandler&&) = delete; + PulledObjectHierarchyHandler& operator=(PulledObjectHierarchyHandler&&) = delete; + + //! Create a PulledObjectHierarchyHandler from a UFE hierarchy handler. + static PulledObjectHierarchyHandler::Ptr + create(const Ufe::HierarchyHandler::Ptr& mayaHierarchyHandler); + + // Ufe::HierarchyHandler overrides + Ufe::Hierarchy::Ptr hierarchy(const Ufe::SceneItem::Ptr& item) const override; + Ufe::SceneItem::Ptr createItem(const Ufe::Path& path) const override; + Ufe::Hierarchy::ChildFilter childFilter() const override; + +private: + Ufe::HierarchyHandler::Ptr _mayaHierarchyHandler; + +}; // PulledObjectHierarchyHandler + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdContextOps.cpp b/lib/mayaUsd/ufe/UsdContextOps.cpp index 3f65790fd9..c8fe2db753 100644 --- a/lib/mayaUsd/ufe/UsdContextOps.cpp +++ b/lib/mayaUsd/ufe/UsdContextOps.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -104,6 +105,11 @@ static const std::string kUSDCylinderPrimImage { "out_USD_Cylinder.png" }; static constexpr char kUSDSpherePrimItem[] = "Sphere"; static constexpr char kUSDSpherePrimLabel[] = "Sphere"; static const std::string kUSDSpherePrimImage { "out_USD_Sphere.png" }; +static constexpr char kEditAsMayaItem[] = "Edit As Maya Data"; +static constexpr char kEditAsMayaLabel[] = "Edit As Maya Data"; +static const std::string kEditAsMayaImage { "edit_as_Maya.png" }; +static constexpr char kCopyAsMayaItem[] = "Copy As Maya Data"; +static constexpr char kCopyAsMayaLabel[] = "Copy As Maya Data"; #if PXR_VERSION >= 2008 static constexpr char kAllRegisteredTypesItem[] = "All Registered"; @@ -563,11 +569,14 @@ Ufe::ContextOps::Items UsdContextOps::getItems(const Ufe::ContextOps::ItemPath& // Top-level item - USD Layer editor (for all context op types). // Only available when building with Qt enabled. items.emplace_back(kUSDLayerEditorItem, kUSDLayerEditorLabel, kUSDLayerEditorImage); - items.emplace_back(Ufe::ContextItem::kSeparator); #endif // Top-level items (do not add for gateway type node): if (!fIsAGatewayType) { + items.emplace_back(kEditAsMayaItem, kEditAsMayaLabel, kEditAsMayaImage); + items.emplace_back(kCopyAsMayaItem, kCopyAsMayaLabel); + items.emplace_back(Ufe::ContextItem::kSeparator); + // Working set management (load and unload): const auto itemLabelPairs = _computeLoadAndUnloadItems(prim()); for (const auto& itemLabelPair : itemLabelPairs) { @@ -610,6 +619,7 @@ Ufe::ContextOps::Items UsdContextOps::getItems(const Ufe::ContextOps::ItemPath& ClearAllReferencesUndoableCommand::commandName, ClearAllReferencesUndoableCommand::commandName); } + } else { if (itemPath[0] == kUSDVariantSetsItem) { UsdVariantSets varSets = prim().GetVariantSets(); @@ -769,6 +779,14 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) return nullptr; return std::make_shared(prim()); + } else if (itemPath[0] == kEditAsMayaItem) { + MString script; + script.format("mayaUsdMenu_pullToDG \"^1s\"", Ufe::PathString::string(path()).c_str()); + MGlobal::executeCommand(script); + } else if (itemPath[0] == kCopyAsMayaItem) { + MString script; + script.format("mayaUsdMenu_copyToDG \"^1s\"", Ufe::PathString::string(path()).c_str()); + MGlobal::executeCommand(script); } return nullptr; diff --git a/lib/mayaUsd/ufe/UsdHierarchy.cpp b/lib/mayaUsd/ufe/UsdHierarchy.cpp index 4231b6d43b..e8a65c4dea 100644 --- a/lib/mayaUsd/ufe/UsdHierarchy.cpp +++ b/lib/mayaUsd/ufe/UsdHierarchy.cpp @@ -45,7 +45,9 @@ #endif #ifdef UFE_V3_FEATURES_AVAILABLE +#include #include +#include // In UFE v2 but only needed for primUpdater. #endif PXR_NAMESPACE_USING_DIRECTIVE @@ -131,17 +133,29 @@ Ufe::SceneItemList UsdHierarchy::filteredChildren(const ChildFilter& childFilter } #endif +// Return UFE child list from input USD child list. Ufe::SceneItemList UsdHierarchy::createUFEChildList(const UsdPrimSiblingRange& range) const { - // Return UFE child list from input USD child list. // Note that the calls to this function are given a range from // getUSDFilteredChildren() above, which ensures that when fItem is a // point instance of a PointInstancer, it will be child-less. As a result, - // we expect to receieve an empty range in that case, and will return an + // we expect to receive an empty range in that case, and will return an // empty scene item list as a result. Ufe::SceneItemList children; + UFE_V3(std::string dagPathStr;) for (const auto& child : range) { - children.emplace_back(UsdSceneItem::create(fItem->path() + child.GetName(), child)); +#ifdef UFE_V3_FEATURES_AVAILABLE + if (PXR_NS::UsdMayaPrimUpdater::readPullInformation(child, dagPathStr)) { + auto item = Ufe::Hierarchy::createItem(Ufe::PathString::path(dagPathStr)); + if (TF_VERIFY(item)) { + children.emplace_back(item); + } + } else { +#endif + children.emplace_back(UsdSceneItem::create(fItem->path() + child.GetName(), child)); +#ifdef UFE_V3_FEATURES_AVAILABLE + } +#endif } return children; } diff --git a/lib/mayaUsd/ufe/UsdPathMappingHandler.cpp b/lib/mayaUsd/ufe/UsdPathMappingHandler.cpp new file mode 100644 index 0000000000..43f5b56661 --- /dev/null +++ b/lib/mayaUsd/ufe/UsdPathMappingHandler.cpp @@ -0,0 +1,131 @@ +// +// 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 "UsdPathMappingHandler.h" + +#include "UsdSceneItem.h" + +#include +#include +#include + +#include + +#include +#include + +#include + +// Needed because of TF_CODING_ERROR +PXR_NAMESPACE_USING_DIRECTIVE + +namespace { +Ufe::Trie fromHostCache; + +class UfeObserver : public Ufe::Observer +{ +public: + UfeObserver() + : Ufe::Observer() + { + } + + void operator()(const Ufe::Notification& notification) override + { + // Any UFE scene notification, we empty the host cache. It will be + // rebuilt on-demand. + fromHostCache.clear(); + } +}; + +Ufe::Observer::Ptr ufeObserver; + +} // namespace + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +UsdPathMappingHandler::UsdPathMappingHandler() + : Ufe::PathMappingHandler() +{ +} + +UsdPathMappingHandler::~UsdPathMappingHandler() +{ + TF_VERIFY(ufeObserver); + Ufe::Scene::instance().removeObserver(ufeObserver); + ufeObserver.reset(); +} + +/*static*/ +UsdPathMappingHandler::Ptr UsdPathMappingHandler::create() +{ + TF_VERIFY(!ufeObserver); + ufeObserver = std::make_shared(); + Ufe::Scene::instance().addObserver(ufeObserver); + + return std::make_shared(); +} + +//------------------------------------------------------------------------------ +// Ufe::PathMappingHandler overrides +//------------------------------------------------------------------------------ + +Ufe::Path UsdPathMappingHandler::toHost(const Ufe::Path& runTimePath) const { return {}; } + +Ufe::Path UsdPathMappingHandler::fromHost(const Ufe::Path& hostPath) const +{ + TF_AXIOM(hostPath.nbSegments() == 1); + + // First look in our cache to see if we've already computed a mapping for the input. + auto found = fromHostCache.find(hostPath); + if (found != nullptr) { + return found->data(); + } + + // Start by getting the dag path from the input host path. The dag path is needed + // to get the pull information (returned as a Ufe::Path) from the plug. + Ufe::Path mayaHostPath(hostPath); + auto dagPath = PXR_NS::UsdMayaUtil::nameToDagPath(hostPath.popHead().string()); + + // Iterate over the dagpath (and its ancestors) looking for any pulled info. + // We keep an array of the Maya node names so we can create a Ufe::PathSegment. + // We build this array in backwards order (and then reverse it) to maintain a + // constant time complexity (for addition). + Ufe::Path mayaMappedPath; + Ufe::PathSegment::Components mayaComps; + while (dagPath.isValid() && (dagPath.length() > 0)) { + TF_AXIOM(!mayaHostPath.empty()); + mayaComps.emplace_back(mayaHostPath.back()); + mayaHostPath = mayaHostPath.pop(); + Ufe::Path ufePath; + if (PXR_NS::UsdMayaPrimUpdater::readPullInformation(dagPath, ufePath)) { + // From the pulled info path, we pop only the last component and replace it with + // the Maya component array. + std::reverse(mayaComps.begin(), mayaComps.end()); + Ufe::PathSegment seg(mayaComps, getUsdRunTimeId(), '/'); + mayaMappedPath = ufePath.pop() + seg; + break; + } + dagPath.pop(); + } + + // Store the computed path mapping (can be empty, if none) in our cache. + fromHostCache.add(hostPath, mayaMappedPath); + return mayaMappedPath; +} + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/ufe/UsdPathMappingHandler.h b/lib/mayaUsd/ufe/UsdPathMappingHandler.h new file mode 100644 index 0000000000..e5ad8a3426 --- /dev/null +++ b/lib/mayaUsd/ufe/UsdPathMappingHandler.h @@ -0,0 +1,54 @@ +#ifndef USDPATHMAPPINGHANDLER_H +#define USDPATHMAPPINGHANDLER_H + +// +// 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 + +#include + +namespace MAYAUSD_NS_DEF { +namespace ufe { + +//! \brief Interface to create a UsdPathMappingHandler interface object. +class MAYAUSD_CORE_PUBLIC UsdPathMappingHandler : public Ufe::PathMappingHandler +{ +public: + typedef std::shared_ptr Ptr; + + UsdPathMappingHandler(); + ~UsdPathMappingHandler() override; + + // Delete the copy/move constructors assignment operators. + UsdPathMappingHandler(const UsdPathMappingHandler&) = delete; + UsdPathMappingHandler& operator=(const UsdPathMappingHandler&) = delete; + UsdPathMappingHandler(UsdPathMappingHandler&&) = delete; + UsdPathMappingHandler& operator=(UsdPathMappingHandler&&) = delete; + + //! Create a UsdPathMappingHandler. + static UsdPathMappingHandler::Ptr create(); + + // Ufe::PathMappingHandler overrides + Ufe::Path toHost(const Ufe::Path&) const override; + Ufe::Path fromHost(const Ufe::Path&) const override; + +}; // UsdPathMappingHandler + +} // namespace ufe +} // namespace MAYAUSD_NS_DEF + +#endif // USDPATHMAPPINGHANDLER_H diff --git a/lib/mayaUsd/ufe/UsdUIUfeObserver.cpp b/lib/mayaUsd/ufe/UsdUIUfeObserver.cpp index f3bdd39a71..66298bf447 100644 --- a/lib/mayaUsd/ufe/UsdUIUfeObserver.cpp +++ b/lib/mayaUsd/ufe/UsdUIUfeObserver.cpp @@ -77,11 +77,23 @@ void UsdUIUfeObserver::operator()(const Ufe::Notification& notification) "mainChannelBox;"); MStringArray paths; if (MGlobal::executeCommand(mainObjListCmd, paths) && (paths.length() > 0)) { + // Under certain circumstances a USD attribute change causes + // the xformOpOrder attribute to change while the channel box + // is displaying a Maya object. This Maya object is returned + // without Maya path component separators (e.g. "Xform2"), + // which triggers UFE single-segment path construction, but + // there is none in Maya for any run-time, so an exception is + // thrown and we crash. Unconditionally refresh the channel + // box for now. PPT, 20-Oct-2021. +#ifdef SINGLE_SEGMENT_PATH_CRASH auto ufePath = Ufe::PathString::path(paths[0].asChar()); if (ufePath.startsWith(ac->path())) { +#endif static const MString updateCBCmd("channelBox -e -update mainChannelBox;"); MGlobal::executeCommand(updateCBCmd); +#ifdef SINGLE_SEGMENT_PATH_CRASH } +#endif } } } diff --git a/lib/mayaUsd/ufe/Utils.cpp b/lib/mayaUsd/ufe/Utils.cpp index eae854c861..af3ae66ddf 100644 --- a/lib/mayaUsd/ufe/Utils.cpp +++ b/lib/mayaUsd/ufe/Utils.cpp @@ -1,5 +1,5 @@ // -// Copyright 2019 Autodesk +// 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. @@ -52,6 +52,10 @@ #include #include +#ifdef UFE_V2_FEATURES_AVAILABLE +#include +#endif + PXR_NAMESPACE_USING_DIRECTIVE namespace { @@ -348,6 +352,37 @@ Ufe::PathSegment dagPathToPathSegment(const MDagPath& dagPath) return Ufe::PathSegment(std::move(components), g_MayaRtid, '|'); } +MDagPath ufeToDagPath(const Ufe::Path& ufePath) +{ + if (ufePath.runTimeId() != g_MayaRtid || +#ifdef UFE_V2_FEATURES_AVAILABLE + ufePath.nbSegments() +#else + ufePath.getSegments().size() +#endif + > 1) { + return MDagPath(); + } + return UsdMayaUtil::nameToDagPath( +#ifdef UFE_V2_FEATURES_AVAILABLE + Ufe::PathString::string(ufePath) +#else + // We have a single segment, so no path segment separator to consider. + ufePath.popHead().string() +#endif + ); +} + +PXR_NS::MayaUsdProxyShapeBase* getProxyShape(const Ufe::Path& path) +{ + // Path should not be empty. + if (!TF_VERIFY(!path.empty())) { + return nullptr; + } + + return g_StageMap.proxyShapeNode(path); +} + UsdTimeCode getTime(const Ufe::Path& path) { // Path should not be empty. diff --git a/lib/mayaUsd/ufe/Utils.h b/lib/mayaUsd/ufe/Utils.h index 2a879fbb16..1d9e1c701f 100644 --- a/lib/mayaUsd/ufe/Utils.h +++ b/lib/mayaUsd/ufe/Utils.h @@ -43,6 +43,10 @@ UFE_NS_DEF class Selection; } +PXR_NAMESPACE_OPEN_SCOPE +class MayaUsdProxyShapeBase; +PXR_NAMESPACE_CLOSE_SCOPE + namespace MAYAUSD_NS_DEF { namespace ufe { @@ -115,6 +119,16 @@ Ufe::Path dagPathToUfe(const MDagPath& dagPath); MAYAUSD_CORE_PUBLIC Ufe::PathSegment dagPathToPathSegment(const MDagPath& dagPath); +//! Convert a single-segment Maya UFE path to a Dag path. If the argument path +//! is not for a Maya object, or if it has more than one segment, returns an +//! invalid MDagPath. +MAYAUSD_CORE_PUBLIC +MDagPath ufeToDagPath(const Ufe::Path& ufePath); + +//! Return the gateway node (i.e. proxy shape) +MAYAUSD_CORE_PUBLIC +PXR_NS::MayaUsdProxyShapeBase* getProxyShape(const Ufe::Path& path); + //! Get the time along the argument path. A gateway node (i.e. proxy shape) //! along the path can transform Maya's time (e.g. with scale and offset). MAYAUSD_CORE_PUBLIC diff --git a/lib/mayaUsd/utils/CMakeLists.txt b/lib/mayaUsd/utils/CMakeLists.txt index 6772cb406d..213477758c 100644 --- a/lib/mayaUsd/utils/CMakeLists.txt +++ b/lib/mayaUsd/utils/CMakeLists.txt @@ -13,6 +13,7 @@ target_sources(${PROJECT_NAME} plugRegistryHelper.cpp selectability.cpp stageCache.cpp + traverseLayer.cpp undoHelperCommand.cpp util.cpp utilFileSystem.cpp @@ -31,6 +32,7 @@ set(HEADERS plugRegistryHelper.h selectability.h stageCache.h + traverseLayer.h undoHelperCommand.h util.h utilFileSystem.h diff --git a/lib/mayaUsd/utils/traverseLayer.cpp b/lib/mayaUsd/utils/traverseLayer.cpp new file mode 100644 index 0000000000..1ff9677bb3 --- /dev/null +++ b/lib/mayaUsd/utils/traverseLayer.cpp @@ -0,0 +1,113 @@ +// +// 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 "traverseLayer.h" + +#include + +#include + +namespace { + +PXR_NAMESPACE_USING_DIRECTIVE + +void _traverseLayer( + const SdfLayerHandle& layer, + const SdfPath& path, + const MayaUsd::TraverseLayerFn& fn +); + +template +void +TraverseChildren( + const SdfLayerHandle& layer, + const SdfPath& path, + const MayaUsd::TraverseLayerFn& fn +) +{ + std::vector children = + layer->GetFieldAs >( + path, ChildPolicy::GetChildrenToken(path)); + + TF_FOR_ALL(i, children) { + _traverseLayer(layer, ChildPolicy::GetChildPath(path, *i), fn); + } +} + +void _traverseLayer( + const SdfLayerHandle& layer, + const SdfPath& path, + const MayaUsd::TraverseLayerFn& fn +) +{ + if (!fn(path)) { + // Prune the traversal as requested by fn. + return; + } + + auto primSpec = layer->GetPrimAtPath(path); + if (!primSpec) { + std::ostringstream msg; + msg << "no primSpec at path " << path.GetText() << "."; + throw MayaUsd::TraversalFailure(msg.str(), path); + } + + auto fields = primSpec->ListFields(); + + TF_FOR_ALL(i, fields) { + if (*i == SdfChildrenKeys->PrimChildren) { + TraverseChildren(layer, path, fn); + } else if (*i == SdfChildrenKeys->PropertyChildren) { + TraverseChildren(layer, path, fn); + } else if (*i == SdfChildrenKeys->MapperChildren) { + TraverseChildren(layer, path, fn); + } else if (*i == SdfChildrenKeys->MapperArgChildren) { + TraverseChildren(layer, path, fn); + } else if (*i == SdfChildrenKeys->VariantChildren) { + TraverseChildren(layer, path, fn); + } else if (*i == SdfChildrenKeys->VariantSetChildren) { + TraverseChildren(layer, path, fn); + } else if (*i == SdfChildrenKeys->ConnectionChildren) { + TraverseChildren(layer, path, fn); + } else if (*i == SdfChildrenKeys->RelationshipTargetChildren) { + TraverseChildren(layer, path, fn); + } else if (*i == SdfChildrenKeys->ExpressionChildren) { + TraverseChildren(layer, path, fn); + } + } +} + +} + +namespace MAYAUSD_NS_DEF { + +bool traverseLayer( + const PXR_NS::SdfLayerHandle& layer, + const PXR_NS::SdfPath& path, + const TraverseLayerFn& fn +) +{ + try { + _traverseLayer(layer, path, fn); + } + catch (const TraversalFailure& e) { + TF_WARN("Layer traversal failed for path %s: %s", + e.path().GetAsString(), e.reason().c_str()); + return false; + } + return true; +} + +} // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/utils/traverseLayer.h b/lib/mayaUsd/utils/traverseLayer.h new file mode 100644 index 0000000000..c9e21606a9 --- /dev/null +++ b/lib/mayaUsd/utils/traverseLayer.h @@ -0,0 +1,74 @@ +// +// 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 MAYAUSD_TRAVERSELAYER_H +#define MAYAUSD_TRAVERSELAYER_H + +#include + +#include + +#include + +namespace MAYAUSD_NS_DEF { + +//! \brief Exception class to signal traversal failure. +// +// Layer traversal functions can throw this exception to signal traversal +// failure, as it is caught by traverseLayer(). + +class TraversalFailure : public std::runtime_error +{ +public: + TraversalFailure(const std::string& reason, const PXR_NS::SdfPath& path) : + std::runtime_error(path.GetAsString()), _reason(reason), _path(path) {} + TraversalFailure(const TraversalFailure&) = default; + ~TraversalFailure() override {} + + std::string reason() const { return _reason; } + PXR_NS::SdfPath path() const { return _path; } + +private: + const std::string _reason; + const PXR_NS::SdfPath _path; +}; + +//! \brief Type definition for layer traversal function. +// +// A layer traversal function must return true to continue the traversal, and +// false to prune traversal to the children of the argument path. The +// traversal function should use the TraversalFailure exception to report +// failure. +typedef std::function TraverseLayerFn; + +/*! \brief Layer traversal utility. + + Pre-order depth-first traversal of layer, starting at path, so that parents + are traversed before children. SdfLayer::Traverse() is depth-first, + post-order, in which case the parent is traversed after the children. + + Catches the TraversalFailure exception, and returns false on traversal + failure. + */ +MAYAUSD_CORE_PUBLIC +bool traverseLayer( + const PXR_NS::SdfLayerHandle& layer, + const PXR_NS::SdfPath& path, + const TraverseLayerFn& fn +); + +} // namespace MAYAUSD_NS_DEF + +#endif diff --git a/lib/mayaUsd/utils/util.cpp b/lib/mayaUsd/utils/util.cpp index 6d7c135a88..4e823770b1 100644 --- a/lib/mayaUsd/utils/util.cpp +++ b/lib/mayaUsd/utils/util.cpp @@ -215,19 +215,6 @@ MStatus UsdMayaUtil::GetMObjectByName(const std::string& nodeName, MObject& mObj return status; } -MStatus UsdMayaUtil::GetDagPathByName(const std::string& nodeName, MDagPath& dagPath) -{ - MSelectionList selectionList; - MStatus status = selectionList.add(MString(nodeName.c_str())); - if (status != MS::kSuccess) { - return status; - } - - status = selectionList.getDagPath(0, dagPath); - - return status; -} - UsdStageRefPtr UsdMayaUtil::GetStageByProxyName(const std::string& proxyPath) { MObject mobj; @@ -2126,8 +2113,11 @@ MDagPath UsdMayaUtil::nameToDagPath(const std::string& name) MSelectionList selection; selection.add(MString(name.c_str())); MDagPath dag; - MStatus status = selection.getDagPath(0, dag); - CHECK_MSTATUS(status); + // Not found? Empty selection list. + if (!selection.isEmpty()) { + MStatus status = selection.getDagPath(0, dag); + CHECK_MSTATUS(status); + } return dag; } diff --git a/lib/mayaUsd/utils/util.h b/lib/mayaUsd/utils/util.h index 16a1bb78b7..ede9c7ac87 100644 --- a/lib/mayaUsd/utils/util.h +++ b/lib/mayaUsd/utils/util.h @@ -185,10 +185,6 @@ MStatus GetMObjectByName(const std::string& nodeName, MObject& mObj); MAYAUSD_CORE_PUBLIC UsdStageRefPtr GetStageByProxyName(const std::string& nodeName); -/// Gets the Maya MDagPath for the node named \p nodeName. -MAYAUSD_CORE_PUBLIC -MStatus GetDagPathByName(const std::string& nodeName, MDagPath& dagPath); - /// Gets the Maya MPlug for the given \p attrPath. /// The attribute path should be specified as "nodeName.attrName" (the format /// used by MEL). diff --git a/lib/usd/translators/CMakeLists.txt b/lib/usd/translators/CMakeLists.txt index df08e03c6f..6e5ec1a5e9 100644 --- a/lib/usd/translators/CMakeLists.txt +++ b/lib/usd/translators/CMakeLists.txt @@ -26,7 +26,6 @@ target_sources(${TARGET_NAME} locatorWriter.cpp materialReader.cpp mayaReferenceReader.cpp - mayaReferenceUpdater.cpp meshReader.cpp meshWriter.cpp meshWriter_BlendShapes.cpp @@ -42,6 +41,14 @@ target_sources(${TARGET_NAME} xformReader.cpp ) +# Edit as Maya requires UFE path mapping. +if(CMAKE_UFE_V3_FEATURES_AVAILABLE) + target_sources(${TARGET_NAME} + PRIVATE + mayaReferenceUpdater.cpp + ) +endif() + add_subdirectory(shading) # ----------------------------------------------------------------------------- @@ -137,4 +144,4 @@ endif() install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${TARGET_NAME} -) \ No newline at end of file +) diff --git a/lib/usd/translators/mayaReferenceReader.cpp b/lib/usd/translators/mayaReferenceReader.cpp index a7681a0464..91d2d47893 100644 --- a/lib/usd/translators/mayaReferenceReader.cpp +++ b/lib/usd/translators/mayaReferenceReader.cpp @@ -31,6 +31,7 @@ // #include #include +#include #include #include @@ -44,16 +45,32 @@ PXR_NAMESPACE_OPEN_SCOPE PXRUSDMAYA_DEFINE_READER(MayaUsd_SchemasMayaReference, args, context) { + MStatus status; + const UsdPrim& usdPrim = args.GetUsdPrim(); MObject parentNode = context.GetMayaNode(usdPrim.GetPath().GetParentPath(), true); - return UsdMayaTranslatorMayaReference::update(usdPrim, parentNode); + + MObject referenceParentNode; + + UsdMayaTranslatorUtil::CreateTransformNode( + usdPrim, parentNode, args, &context, &status, &referenceParentNode); + + return UsdMayaTranslatorMayaReference::update(usdPrim, referenceParentNode); } PXRUSDMAYA_DEFINE_READER(MayaUsd_SchemasALMayaReference, args, context) { + MStatus status; + const UsdPrim& usdPrim = args.GetUsdPrim(); MObject parentNode = context.GetMayaNode(usdPrim.GetPath().GetParentPath(), true); - return UsdMayaTranslatorMayaReference::update(usdPrim, parentNode); + + MObject referenceParentNode; + + UsdMayaTranslatorUtil::CreateTransformNode( + usdPrim, parentNode, args, &context, &status, &referenceParentNode); + + return UsdMayaTranslatorMayaReference::update(usdPrim, referenceParentNode); } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/usd/translators/mayaReferenceUpdater.cpp b/lib/usd/translators/mayaReferenceUpdater.cpp index dc0f09b286..0ae8617e29 100644 --- a/lib/usd/translators/mayaReferenceUpdater.cpp +++ b/lib/usd/translators/mayaReferenceUpdater.cpp @@ -25,8 +25,15 @@ #include #include #include +#include +#include +#include +#include #include +#include +#include #include +#include #include #include @@ -34,37 +41,70 @@ PXR_NAMESPACE_OPEN_SCOPE PXRUSDMAYA_REGISTER_UPDATER( MayaUsd_SchemasMayaReference, + reference, PxrUsdTranslators_MayaReferenceUpdater, - (UsdMayaPrimUpdater::Supports::Push | UsdMayaPrimUpdater::Supports::Clear)); + (UsdMayaPrimUpdater::Supports::Push | UsdMayaPrimUpdater::Supports::Clear + | UsdMayaPrimUpdater::Supports::AutoPull)); PXRUSDMAYA_REGISTER_UPDATER( MayaUsd_SchemasALMayaReference, + reference, PxrUsdTranslators_MayaReferenceUpdater, - (UsdMayaPrimUpdater::Supports::Push | UsdMayaPrimUpdater::Supports::Clear)); + (UsdMayaPrimUpdater::Supports::Push | UsdMayaPrimUpdater::Supports::Clear + | UsdMayaPrimUpdater::Supports::AutoPull)); PxrUsdTranslators_MayaReferenceUpdater::PxrUsdTranslators_MayaReferenceUpdater( const MFnDependencyNode& depNodeFn, - const SdfPath& usdPath) - : UsdMayaPrimUpdater(depNodeFn, usdPath) + const Ufe::Path& path) + : UsdMayaPrimUpdater(depNodeFn, path) { } /* virtual */ -bool PxrUsdTranslators_MayaReferenceUpdater::Pull(UsdMayaPrimUpdaterContext* context) +bool PxrUsdTranslators_MayaReferenceUpdater::PushCopySpecs( + SdfLayerRefPtr srcLayer, + const SdfPath& srcSdfPath, + SdfLayerRefPtr dstLayer, + const SdfPath& dstSdfPath) { - const UsdPrim& usdPrim = GetUsdPrim(*context); - const MObject& parentNode = GetMayaObject(); + bool success = false; + + // We are looking for a very specific configuration in here + // i.e. a parent prim with a variant set called "animVariant" + // and two variants "cache" and "rig" + UsdStageRefPtr srcStage = UsdStage::Open(dstLayer); + SdfPath parentSdfPath = dstSdfPath.GetParentPath(); + UsdPrim primWithVariant = srcStage->GetPrimAtPath(parentSdfPath); + + // Switching variant to cache to discover payload and where to write the data + UsdVariantSet variantSet = primWithVariant.GetVariantSet("animVariant"); + variantSet.SetVariantSelection("cache"); - UsdMayaTranslatorMayaReference::update(usdPrim, parentNode); + // Find the layer and prim to which we need to copy the content of push + // There is currently no easy way to query this information at prim level + // so we go to pcp and sdf. + UsdPrim primWithPayload = primWithVariant.GetChildren().front(); + UsdPrimCompositionQuery query(primWithPayload); + for (const auto& arc : query.GetCompositionArcs()) { + if (arc.GetArcType() == PcpArcTypePayload) { + SdfLayerHandle payloadLayer + = arc.GetTargetNode().GetLayerStack()->GetIdentifier().rootLayer; + SdfPath payloadPrimPath = arc.GetTargetNode().GetPath(); + success = SdfCopySpec(srcLayer, srcSdfPath, payloadLayer, payloadPrimPath); - return true; + break; + } + } + + return success; } /* virtual */ -void PxrUsdTranslators_MayaReferenceUpdater::Clear(UsdMayaPrimUpdaterContext* context) +bool PxrUsdTranslators_MayaReferenceUpdater::DiscardEdits(const UsdMayaPrimUpdaterContext& context) { const MObject& parentNode = GetMayaObject(); - UsdMayaTranslatorMayaReference::UnloadMayaReference(parentNode); + + return UsdMayaPrimUpdater::DiscardEdits(context); } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/usd/translators/mayaReferenceUpdater.h b/lib/usd/translators/mayaReferenceUpdater.h index d6f1352553..d45386d259 100644 --- a/lib/usd/translators/mayaReferenceUpdater.h +++ b/lib/usd/translators/mayaReferenceUpdater.h @@ -26,18 +26,24 @@ PXR_NAMESPACE_OPEN_SCOPE class SdfPath; -/// Exports Maya cameras to UsdGeomCamera. +/// Pull & Push support for MayaReference class PxrUsdTranslators_MayaReferenceUpdater : public UsdMayaPrimUpdater { public: PxrUsdTranslators_MayaReferenceUpdater( const MFnDependencyNode& depNodeFn, - const SdfPath& usdPath); + const Ufe::Path& path); - bool Pull(UsdMayaPrimUpdaterContext* context) override; - void Clear(UsdMayaPrimUpdaterContext* context) override; + MAYAUSD_CORE_PUBLIC + bool DiscardEdits(const UsdMayaPrimUpdaterContext& context) override; protected: + MAYAUSD_CORE_PUBLIC + bool PushCopySpecs( + SdfLayerRefPtr srcLayer, + const SdfPath& srcSdfPath, + SdfLayerRefPtr dstLayer, + const SdfPath& dstSdfPath) override; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/plugin/adsk/plugin/exportTranslator.cpp b/plugin/adsk/plugin/exportTranslator.cpp index 04ff34346b..7970973250 100644 --- a/plugin/adsk/plugin/exportTranslator.cpp +++ b/plugin/adsk/plugin/exportTranslator.cpp @@ -125,8 +125,7 @@ MStatus UsdMayaExportTranslator::writer( const std::string exportRootPath = exportRootStrings[idxRoot].asChar(); if (!exportRootPath.empty()) { - MDagPath rootDagPath; - UsdMayaUtil::GetDagPathByName(exportRootPath, rootDagPath); + MDagPath rootDagPath = UsdMayaUtil::nameToDagPath(exportRootPath); if (!rootDagPath.isValid()) { MGlobal::displayError( MString("Invalid dag path provided for export root: ") diff --git a/plugin/adsk/plugin/plugin.cpp b/plugin/adsk/plugin/plugin.cpp index ec635fa63b..ddfedb5cdd 100644 --- a/plugin/adsk/plugin/plugin.cpp +++ b/plugin/adsk/plugin/plugin.cpp @@ -70,6 +70,10 @@ #include #endif +#ifdef UFE_V3_FEATURES_AVAILABLE +#include +#endif + #if defined(WANT_QT_BUILD) #include #endif @@ -322,6 +326,11 @@ MStatus initializePlugin(MObject obj) UsdMayaSceneResetNotice::InstallListener(); UsdMayaDiagnosticDelegate::InstallDelegate(); +#ifdef UFE_V3_FEATURES_AVAILABLE + // Install notifications + PrimUpdaterManager::GetInstance(); +#endif + return status; } diff --git a/plugin/adsk/scripts/CMakeLists.txt b/plugin/adsk/scripts/CMakeLists.txt index 1b0fbdb1b3..32e3819f5f 100644 --- a/plugin/adsk/scripts/CMakeLists.txt +++ b/plugin/adsk/scripts/CMakeLists.txt @@ -17,6 +17,7 @@ list(APPEND scripts_src mayaUsd_pluginBatchLoad.mel mayaUsd_pluginBatchUnload.mel usdFileSaveOptions.mel + USDMenuProc.mel ) install(FILES ${scripts_src} diff --git a/plugin/adsk/scripts/USDMenuProc.mel b/plugin/adsk/scripts/USDMenuProc.mel new file mode 100644 index 0000000000..9755a84ba7 --- /dev/null +++ b/plugin/adsk/scripts/USDMenuProc.mel @@ -0,0 +1,50 @@ +// 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. +// + +global proc mayaUsdMenu_pullToDG(string $obj) +{ + if (size($obj) == 0) { + string $nonMayaObjs[] = `python("import maya.internal.ufeSupport.utils as ufeUtils; ufeUtils.getNonMayaSelectedItems()")`; + $obj = $nonMayaObjs[0]; + } + if (size($obj) != 0) { + python("from mayaUsd.lib import PrimUpdaterManager; import ufe; PrimUpdaterManager.pull('" + $obj + "');"); + } +} + +global proc mayaUsdMenu_copyToDG( string $ufePath ) +{ + if (size($ufePath) == 0) { + string $nonMayaObjs[] = `python("import maya.internal.ufeSupport.utils as ufeUtils; ufeUtils.getNonMayaSelectedItems()")`; + $ufePath = $nonMayaObjs[0]; + } + + if (size($ufePath)) { + python("from mayaUsd.lib import PrimUpdaterManager; import maya.cmds as cmds; PrimUpdaterManager.copy('" + $ufePath + "', '|world');"); + } +} +global proc USDMenuProc(string $parent, string $obj) +{ + int $haveNonMaya = `python("import maya.internal.ufeSupport.utils as ufeUtils; ufeUtils.hasNonMayaSelectedItems()")`; + if ($haveNonMaya) + { + popupMenu -e -dai $parent; + setParent -menu $parent; + + setParent -menu ..; + menuItem -label "Edit As Maya Data" -image "edit_as_Maya.png" -command ("mayaUsdMenu_pullToDG \"" + $obj + "\""); + menuItem -label "Copy As Maya Data" -command ("mayaUsdMenu_copyToDG \"" + $obj + "\""); + } +} diff --git a/plugin/adsk/scripts/mayaUSDRegisterStrings.mel b/plugin/adsk/scripts/mayaUSDRegisterStrings.mel index 45c351545a..bc2db35790 100644 --- a/plugin/adsk/scripts/mayaUSDRegisterStrings.mel +++ b/plugin/adsk/scripts/mayaUSDRegisterStrings.mel @@ -50,6 +50,7 @@ global proc mayaUSDRegisterStrings() { register("kMenuClear", "Clear"); register("kMenuRevertToFile", "Revert to File"); register("kMenuLayerEditor", "USD Layer Editor"); + register("kContextMenuLayerEditor", "USD Layer Editor..."); register("kMenuLayerEditorAnn", "Organize and edit USD data in layers"); register("kMenuLoadSublayers", "Load Sublayers..."); register("kMenuMute", "Mute"); diff --git a/plugin/adsk/scripts/mayaUsdMenu.mel b/plugin/adsk/scripts/mayaUsdMenu.mel index a53347538d..0155e5ad35 100644 --- a/plugin/adsk/scripts/mayaUsdMenu.mel +++ b/plugin/adsk/scripts/mayaUsdMenu.mel @@ -555,6 +555,150 @@ proc termAEShowMenu() removeMenuCallback($aeShowMenu, "mayaUsdMenu_aeShowMenuCallback"); } +/////////////////////////////////////////////////////////////////////////////// +// initPullMenu +// Marking menu with USD options for DAG objects + +proc int isPulledUsdObject(string $dagObject) { + string $pullAttribute = ( $dagObject + ".Pull_UfePath" ); + if (`attributeExists "Pull_UfePath" $dagObject`) { + string $ufePath = `getAttr $pullAttribute`; + if ($ufePath != "") { + return 1; + } + } + + return 0; +} + +proc int isObjectSelected(string $obj) +{ + if (size($obj)) { + string $sel[] = `ls -sl`; + return stringArrayContains($obj, $sel); + } + return 0; +} + +global proc mayaUsdMenu_pushBackToUSD(string $obj) +{ + if (size($obj) == 0) { + string $sel[] = `ls -sl`; + if (size($sel)) { + $obj = $sel[0]; + } + } + if (size($obj)) { + python("from mayaUsd.lib import PrimUpdaterManager; PrimUpdaterManager.push('" + $obj + "');"); + } +} + +global proc mayaUsdMenu_pushClear(string $obj) +{ + python("from mayaUsd.lib import PrimUpdaterManager; PrimUpdaterManager.discardEdits('" + $obj + "');"); +} + +global proc mayaUsdMenu_copyToUSD( string $proxy, string $obj ) +{ + if (size($obj) == 0) { + string $sel[] = `ls -sl`; + if (size($sel)) { + $obj = $sel[0]; + } + } + string $objLong[] = `ls -l $obj`; + string $proxyLong[] = `ls -l $proxy`; + if (size($objLong) && size($proxyLong)) { + python("from mayaUsd.lib import PrimUpdaterManager; import maya.cmds as cmds; PrimUpdaterManager.copy('" + $objLong[0] + "', '"+ $proxyLong[0] +"');"); + } +} + +global proc mayaUsdMenu_markingMenuCallback( string $obj ) +{ + if (isPulledUsdObject($obj)) { + string $pushback = `menuItem -label "Merge Maya Edits to USD" -insertAfter "" -image "merge_to_USD.png" -command ("mayaUsdMenu_pushBackToUSD " + $obj)`; + string $pushclear = `menuItem -label "Discard Maya Edits" -insertAfter $pushback -image "discard_edits.png" -command ("mayaUsdMenu_pushClear " + $obj)`; + menuItem -divider true -insertAfter $pushclear; + } + else { + string $proxyShapes[] = `ls -type mayaUsdProxyShape`; + if (size($proxyShapes) > 0) { + menuItem -divider true -insertAfter ""; + menuItem -label "Copy To" -subMenu true -insertAfter ""; + for ($proxy in $proxyShapes) { + menuItem -label $proxy -command ("mayaUsdMenu_copyToUSD " + $proxy + " " + $obj); + } + } + } +} + +proc initPullMenu() +{ + callbacks -addCallback mayaUsdMenu_markingMenuCallback -hook addRMBBakingMenuItems -owner mayaUsdPlugin; +} + +proc tearPullMenu() +{ + callbacks -removeCallback mayaUsdMenu_markingMenuCallback -hook addRMBBakingMenuItems -owner mayaUsdPlugin; +} + +/////////////////////////////////////////////////////////////////////////////// +// Outliner Popup Menu +// +proc initOutlinerPopupMenu() +{ + callbacks -addCallback mayaUsdMenu_OutlinerPopupCallback -hook addItemsToOutlinerNodePopupMenu -owner mayaUsdPlugin; +} + +proc termOutlinerPopupMenu() +{ + callbacks -removeCallback mayaUsdMenu_OutlinerPopupCallback -hook addItemsToOutlinerNodePopupMenu -owner mayaUsdPlugin; +} + +proc int isMayaUsdProxyShape(string $nodeName) +{ + if (size($nodeName)) { + // We use inherited here to find the proxy shape base so this will + // work on other derived proxy shape types. + string $inher[] = `nodeType -inherited $nodeName`; + if (stringArrayContains("mayaUsdProxyShapeBase", $inher)) { + return 1; + } + } + return 0; +} + +global proc mayaUsdMenu_OutlinerPopupCallback( string $nodeName ) +{ + if ((size($nodeName) == 0) || !`objExists $nodeName`) { + return; + } + + int $isProxyNode = 0; + if (`nodeType $nodeName` == "transform") { + string $shapes[] = `listRelatives -shapes -fullPath $nodeName`; + if (`size($shapes)` > 0) { + string $shapeNode = $shapes[0]; + if (isMayaUsdProxyShape($shapeNode)) { + $isProxyNode = 1; + if (`exists mayaUsdLayerEditorWindow`) { + string $command = "mayaUsdLayerEditorWindow -proxyShape \"" + $shapeNode + "\" mayaUsdLayerEditor"; + menuItem -divider true -insertAfter ""; + menuItem -enableCommandRepeat false + -label `getMayaUsdString("kContextMenuLayerEditor")` + -insertAfter "" + -command $command openUsdLayerEditor; + } + } + } + } + + // Don't add the "Push..." menu items for the proxy node or the shape. + if (!$isProxyNode && !isMayaUsdProxyShape($nodeName)) { + mayaUsdMenu_markingMenuCallback($nodeName); + } +} + /////////////////////////////////////////////////////////////////////////////// // mayaUsdMenu_loadui // main entry point on plugin load @@ -572,6 +716,9 @@ global proc mayaUsdMenu_loadui() { if(!mayaUSD_isSelectionKindValid()) { mayaUsdSelectKindNone(); } + + initPullMenu(); + initOutlinerPopupMenu(); } /////////////////////////////////////////////////////////////////////////////// @@ -581,6 +728,8 @@ global proc mayaUsdMenu_unloadui() { termCreateMenu(); termSelectMenu(); termAEShowMenu(); + tearPullMenu(); + termOutlinerPopupMenu(); } /////////////////////////////////////////////////////////////////////////////// diff --git a/plugin/adsk/scripts/mayaUsd_pluginUICreation.mel b/plugin/adsk/scripts/mayaUsd_pluginUICreation.mel index 8d6e270702..0ac092b9a9 100644 --- a/plugin/adsk/scripts/mayaUsd_pluginUICreation.mel +++ b/plugin/adsk/scripts/mayaUsd_pluginUICreation.mel @@ -22,4 +22,6 @@ global proc mayaUsd_pluginUICreation() warning ("Error loading ufeSupport.py."); } } + + source "USDMenuProc.mel"; } diff --git a/plugin/pxr/maya/lib/usdMaya/exportTranslator.cpp b/plugin/pxr/maya/lib/usdMaya/exportTranslator.cpp index a19e78014b..3266a5d4ef 100644 --- a/plugin/pxr/maya/lib/usdMaya/exportTranslator.cpp +++ b/plugin/pxr/maya/lib/usdMaya/exportTranslator.cpp @@ -100,8 +100,7 @@ MStatus UsdMayaExportTranslator::writer( const std::string exportRootPath = exportRootStrings[idxRoot].asChar(); if (!exportRootPath.empty()) { - MDagPath rootDagPath; - UsdMayaUtil::GetDagPathByName(exportRootPath, rootDagPath); + MDagPath rootDagPath = UsdMayaUtil::nameToDagPath(exportRootPath); if (!rootDagPath.isValid()) { MGlobal::displayError( MString("Invalid dag path provided for export root: ") diff --git a/test/lib/mayaUsd/fileio/CMakeLists.txt b/test/lib/mayaUsd/fileio/CMakeLists.txt index 73581ccf9e..3189605f6d 100644 --- a/test/lib/mayaUsd/fileio/CMakeLists.txt +++ b/test/lib/mayaUsd/fileio/CMakeLists.txt @@ -5,6 +5,14 @@ set(TEST_SCRIPT_FILES testImportChaser.py ) +if(CMAKE_UFE_V3_FEATURES_AVAILABLE) + list(APPEND TEST_SCRIPT_FILES + testEditAsMaya.py + testMergeToUsd.py + testDiscardEdits.py + ) +endif() + # requires MaterialX support if (MAYA_APP_VERSION VERSION_GREATER 2022 AND PXR_VERSION VERSION_GREATER 2102) list(APPEND TEST_SCRIPT_FILES @@ -18,6 +26,8 @@ foreach(script ${TEST_SCRIPT_FILES}) mayaUsd_add_test(${target} PYTHON_MODULE ${target} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ENV + "UFE_PREVIEW_VERSION_NUM=${UFE_PREVIEW_VERSION_NUM}" ) # Add a ctest label to these tests for easy filtering. diff --git a/test/lib/mayaUsd/fileio/testDiscardEdits.py b/test/lib/mayaUsd/fileio/testDiscardEdits.py new file mode 100644 index 0000000000..f1b9ebc0ac --- /dev/null +++ b/test/lib/mayaUsd/fileio/testDiscardEdits.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python + +# +# 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. +# + +import fixturesUtils + +from mayaUtils import setMayaTranslation +from usdUtils import createSimpleXformScene + +import mayaUsd.lib + +import mayaUtils +import mayaUsd.ufe + +from pxr import UsdGeom, Gf + +from maya import cmds +from maya import standalone +from maya.api import OpenMaya as om + +import ufe + +import unittest + +from testUtils import assertVectorAlmostEqual + +import os + +class MergeToUsdTestCase(unittest.TestCase): + '''Test discard Maya edits: discard edits done as Maya data to USD prims. + ''' + + pluginsLoaded = False + + @classmethod + def setUpClass(cls): + fixturesUtils.readOnlySetUpClass(__file__, loadPlugin=False) + + if not cls.pluginsLoaded: + cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded() + + @classmethod + def tearDownClass(cls): + standalone.uninitialize() + + def setUp(self): + cmds.file(new=True, force=True) + + @unittest.skipIf(os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') < '3006', 'Test only available in UFE preview version 0.3.6 and greater') + def testDiscardEdits(self): + '''Discard edits on a USD transform.''' + + # Edit as Maya first. + (ps, xlateOp, usdXlation, aUsdUfePathStr, aUsdUfePath, aUsdItem, + _, _, _, _, _) = createSimpleXformScene() + self.assertTrue(mayaUsd.lib.PrimUpdaterManager.editAsMaya(aUsdUfePathStr)) + aMayaItem = ufe.GlobalSelection.get().front() + mayaXlation = om.MVector(4, 5, 6) + (aMayaPath, aMayaPathStr, aFn, mayaMatrix) = \ + setMayaTranslation(aMayaItem, mayaXlation) + + self.assertEqual(aFn.translation(om.MSpace.kObject), mayaXlation) + + mayaToUsd = ufe.PathMappingHandler.pathMappingHandler(aMayaItem) + self.assertEqual(mayaToUsd.fromHost(aMayaPath), aUsdUfePath) + + psHier = ufe.Hierarchy.hierarchy(ps) + self.assertNotIn(aUsdItem, psHier.children()) + self.assertIn(aMayaItem, psHier.children()) + + # Discard Maya edits. + self.assertTrue(mayaUsd.lib.PrimUpdaterManager.discardEdits(aMayaPathStr)) + + # Original USD translation values are preserved. + usdMatrix = xlateOp.GetOpTransform(mayaUsd.ufe.getTime(aUsdUfePathStr)) + self.assertEqual(usdMatrix.ExtractTranslation(), usdXlation) + + # There no longer is a path mapping from the Maya path to the USD path. + self.assertEqual(len(mayaToUsd.fromHost(aMayaPath)), 0) + + # Hierarchy is restored: USD item is child of proxy shape, Maya item is + # not. Be careful to use the Maya path rather than the Maya item, which + # should no longer exist. + self.assertIn(aUsdItem, psHier.children()) + self.assertNotIn(aMayaPath, [child.path() for child in psHier.children()]) + + # Maya node is removed. + with self.assertRaises(RuntimeError): + om.MSelectionList().add(aMayaPathStr) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/test/lib/mayaUsd/fileio/testEditAsMaya.py b/test/lib/mayaUsd/fileio/testEditAsMaya.py new file mode 100644 index 0000000000..df1366fc27 --- /dev/null +++ b/test/lib/mayaUsd/fileio/testEditAsMaya.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python + +# +# 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. +# + +import fixturesUtils + +from usdUtils import createSimpleXformScene + +import mayaUsd.lib + +import mayaUtils +import mayaUsd.ufe + +from pxr import UsdGeom, Gf + +from maya import cmds +from maya import standalone +from maya.api import OpenMaya as om + +import ufe + +import unittest + +from testUtils import assertVectorAlmostEqual + +import os + +class EditAsMayaTestCase(unittest.TestCase): + '''Test edit as Maya: bring USD data into Maya to edit. + + Also known as Pull to DG. + ''' + + pluginsLoaded = False + + @classmethod + def setUpClass(cls): + fixturesUtils.readOnlySetUpClass(__file__, loadPlugin=False) + + if not cls.pluginsLoaded: + cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded() + + @classmethod + def tearDownClass(cls): + standalone.uninitialize() + + def setUp(self): + cmds.file(new=True, force=True) + + @unittest.skipIf(os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') < '3006', 'Test only available in UFE preview version 0.3.6 and greater') + def testTransformEditAsMaya(self): + '''Edit a USD transform as a Maya object.''' + + (ps, xlateOp, xlation, aUsdUfePathStr, aUsdUfePath, aUsdItem, + _, _, _, _, _) = createSimpleXformScene() + + # Edit aPrim as Maya data. + self.assertTrue(mayaUsd.lib.PrimUpdaterManager.editAsMaya(aUsdUfePathStr)) + + # Test the path mapping services. + # + # Unfortunately, toHost is unimplemented as of 20-Sep-2021. Use the + # selection to retrieve the Maya object. + # + # usdToMaya = ufe.PathMappingHandler.pathMappingHandler(aUsdItem) + # aMayaUfePath = usdToMaya.toHost(aUsdUfePath) + # self.assertEqual(aMayaUfePath.nbSegments(), 1) + + aMayaItem = ufe.GlobalSelection.get().front() + aMayaPath = aMayaItem.path() + self.assertEqual(aMayaPath.nbSegments(), 1) + mayaToUsd = ufe.PathMappingHandler.pathMappingHandler(aMayaItem) + self.assertEqual(mayaToUsd.fromHost(aMayaPath), aUsdUfePath) + + # Confirm the hierarchy is preserved through the Hierarchy interface: + # one child, the parent of the pulled item is the proxy shape, and + # the proxy shape has the pulled item as a child, not the original USD + # scene item. + aMayaHier = ufe.Hierarchy.hierarchy(aMayaItem) + self.assertEqual(len(aMayaHier.children()), 1) + self.assertEqual(ps, aMayaHier.parent()) + psHier = ufe.Hierarchy.hierarchy(ps) + self.assertIn(aMayaItem, psHier.children()) + self.assertNotIn(aUsdItem, psHier.children()) + + # Confirm the translation has been transferred, and that the local + # transformation is only a translation. + aDagPath = om.MSelectionList().add(ufe.PathString.string(aMayaPath)).getDagPath(0) + aFn= om.MFnTransform(aDagPath) + self.assertEqual(aFn.translation(om.MSpace.kObject), om.MVector(*xlation)) + mayaMatrix = aFn.transformation().asMatrix() + usdMatrix = xlateOp.GetOpTransform(mayaUsd.ufe.getTime(aUsdUfePathStr)) + mayaValues = [v for v in mayaMatrix] + usdValues = [v for row in usdMatrix for v in row] + + assertVectorAlmostEqual(self, mayaValues, usdValues) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/test/lib/mayaUsd/fileio/testMergeToUsd.py b/test/lib/mayaUsd/fileio/testMergeToUsd.py new file mode 100644 index 0000000000..2ae09eb02d --- /dev/null +++ b/test/lib/mayaUsd/fileio/testMergeToUsd.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python + +# +# 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. +# + +import fixturesUtils + +from mayaUtils import setMayaTranslation +from usdUtils import createSimpleXformScene + +import mayaUsd.lib + +import mayaUtils +import mayaUsd.ufe + +from pxr import UsdGeom, Gf + +from maya import cmds +from maya import standalone +from maya.api import OpenMaya as om + +import ufe + +import unittest + +from testUtils import assertVectorAlmostEqual + +import os + +class MergeToUsdTestCase(unittest.TestCase): + '''Test merge to USD: commit edits done as Maya data back into USD. + + Also known as Push to USD. + ''' + + pluginsLoaded = False + + @classmethod + def setUpClass(cls): + fixturesUtils.readOnlySetUpClass(__file__, loadPlugin=False) + + if not cls.pluginsLoaded: + cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded() + + @classmethod + def tearDownClass(cls): + standalone.uninitialize() + + def setUp(self): + cmds.file(new=True, force=True) + + @unittest.skipIf(os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') < '3006', 'Test only available in UFE preview version 0.3.6 and greater') + def testTransformMergeToUsd(self): + '''Merge edits on a USD transform back to USD.''' + + # To merge back to USD, we must edit as Maya first. + (ps, aXlateOp, _, aUsdUfePathStr, aUsdUfePath, aUsdItem, + bXlateOp, _, bUsdUfePathStr, bUsdUfePath, bUsdItem) = \ + createSimpleXformScene() + self.assertTrue(mayaUsd.lib.PrimUpdaterManager.editAsMaya(aUsdUfePathStr)) + aMayaItem = ufe.GlobalSelection.get().front() + (aMayaPath, aMayaPathStr, _, aMayaMatrix) = \ + setMayaTranslation(aMayaItem, om.MVector(4, 5, 6)) + + bMayaPathStr = aMayaPathStr + '|B' + bMayaPath = ufe.PathString.path(bMayaPathStr) + bMayaItem = ufe.Hierarchy.createItem(bMayaPath) + (_, _, _, bMayaMatrix) = \ + setMayaTranslation(bMayaItem, om.MVector(10, 11, 12)) + + # There should be a path mapping for the edit as Maya hierarchy. + for (mayaItem, mayaPath, usdUfePath) in \ + zip([aMayaItem, bMayaItem], [aMayaPath, bMayaPath], + [aUsdUfePath, bUsdUfePath]): + mayaToUsd = ufe.PathMappingHandler.pathMappingHandler(mayaItem) + self.assertEqual(mayaToUsd.fromHost(mayaPath), usdUfePath) + + psHier = ufe.Hierarchy.hierarchy(ps) + + # Merge edits back to USD. + self.assertTrue(mayaUsd.lib.PrimUpdaterManager.mergeToUsd(aMayaPathStr)) + + # Check that edits have been preserved in USD. + for (usdUfePathStr, mayaMatrix, xlateOp) in \ + zip([aUsdUfePathStr, bUsdUfePathStr], [aMayaMatrix, bMayaMatrix], + [aXlateOp, bXlateOp]): + usdMatrix = xlateOp.GetOpTransform( + mayaUsd.ufe.getTime(usdUfePathStr)) + mayaValues = [v for v in mayaMatrix] + usdValues = [v for row in usdMatrix for v in row] + + assertVectorAlmostEqual(self, mayaValues, usdValues) + + # There no longer are any Maya to USD path mappings. + for mayaPath in [aMayaPath, bMayaPath]: + self.assertEqual(len(mayaToUsd.fromHost(mayaPath)), 0) + + # Hierarchy is restored: USD item is child of proxy shape, Maya item is + # not. Be careful to use the Maya path rather than the Maya item, which + # should no longer exist. + self.assertIn(aUsdItem, psHier.children()) + self.assertNotIn(aMayaPath, [child.path() for child in psHier.children()]) + + # Maya nodes are removed. + for mayaPathStr in [aMayaPathStr, bMayaPathStr]: + with self.assertRaises(RuntimeError): + om.MSelectionList().add(mayaPathStr) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/test/lib/ufe/testGroupCmd.py b/test/lib/ufe/testGroupCmd.py index c8896f8096..e79643899b 100644 --- a/test/lib/ufe/testGroupCmd.py +++ b/test/lib/ufe/testGroupCmd.py @@ -155,6 +155,12 @@ def testUsdGroup(self): groupPath = ufe.Path([mayaPathSegment, usdUtils.createUfePathSegment("/Ball_set/Props/newGroup1")]) self.assertEqual(globalSelection.front(), ufe.Hierarchy.createItem(groupPath)) + # Group object (a.k.a parent) will be added to selection list. This behavior matches the native Maya group command. + globalSelection = ufe.GlobalSelection.get() + + groupPath = ufe.Path([mayaPathSegment, usdUtils.createUfePathSegment("/Ball_set/Props/newGroup1")]) + self.assertEqual(globalSelection.front(), ufe.Hierarchy.createItem(groupPath)) + parentChildrenPost = parentHierarchy.children() self.assertEqual(len(parentChildrenPost), 5) @@ -182,7 +188,7 @@ def testUsdGroup(self): else: groupCmd.undo() - # gloabl selection should not be empty after undo. + # global selection should not be empty after undo. if (os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') > '3004'): self.assertEqual(len(globalSelection), 2) else: diff --git a/test/testUtils/mayaUtils.py b/test/testUtils/mayaUtils.py index 9003ffb897..20b1664fe2 100644 --- a/test/testUtils/mayaUtils.py +++ b/test/testUtils/mayaUtils.py @@ -24,6 +24,7 @@ from mayaUsd import ufe as mayaUsdUfe from maya import cmds +from maya.api import OpenMaya as om import ufe import ufeUtils, testUtils @@ -185,6 +186,16 @@ def openCompositionArcsScene(): def openPrimPathScene(): return openTestScene("primPath", "primPath.ma" ) +def setMayaTranslation(aMayaItem, t): + '''Set the translation on the argument Maya scene item.''' + + aMayaPath = aMayaItem.path() + aMayaPathStr = ufe.PathString.string(aMayaPath) + aDagPath = om.MSelectionList().add(aMayaPathStr).getDagPath(0) + aFn= om.MFnTransform(aDagPath) + aFn.setTranslation(t, om.MSpace.kObject) + return (aMayaPath, aMayaPathStr, aFn, aFn.transformation().asMatrix()) + def createProxyAndStage(): """ Create in-memory stage diff --git a/test/testUtils/usdUtils.py b/test/testUtils/usdUtils.py index 7f7de15d3c..d3c8f4552a 100644 --- a/test/testUtils/usdUtils.py +++ b/test/testUtils/usdUtils.py @@ -21,12 +21,13 @@ """ import mayaUsd.ufe +import mayaUsd.lib +import mayaUsd_createStageWithNewLayer import ufe import ufeUtils -from pxr import Usd -from pxr import UsdGeom +from pxr import Usd, UsdGeom, Gf usdSeparator = '/' @@ -81,3 +82,36 @@ def createAnimatedHierarchy(stage): UsdGeom.XformCommonAPI(parentPrimA).SetTranslate((0,5,0),time2) UsdGeom.XformCommonAPI(childPrimSphere).SetTranslate((-5,0,0),time2) UsdGeom.XformCommonAPI(childPrimCube).SetTranslate((0,0,-5),time2) + +def createSimpleXformScene(): + '''Create a simple scene with a trivial hierarchy: + + A translation (1, 2, 3) + |_B translation (7, 8, 9) + + ''' + + psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + psPath = ufe.PathString.path(psPathStr) + ps = ufe.Hierarchy.createItem(psPath) + stage = mayaUsd.lib.GetPrim(psPathStr).GetStage() + aPrim = stage.DefinePrim('/A', 'Xform') + aXformable = UsdGeom.Xformable(aPrim) + aXlateOp = aXformable.AddTranslateOp() + aXlation = Gf.Vec3d(1, 2, 3) + aXlateOp.Set(aXlation) + aUsdUfePathStr = psPathStr + ',/A' + aUsdUfePath = ufe.PathString.path(aUsdUfePathStr) + aUsdItem = ufe.Hierarchy.createItem(aUsdUfePath) + + bPrim = stage.DefinePrim('/A/B', 'Xform') + bXformable = UsdGeom.Xformable(bPrim) + bXlateOp = bXformable.AddTranslateOp() + bXlation = Gf.Vec3d(7, 8, 9) + bXlateOp.Set(bXlation) + bUsdUfePathStr = aUsdUfePathStr + '/B' + bUsdUfePath = ufe.PathString.path(bUsdUfePathStr) + bUsdItem = ufe.Hierarchy.createItem(bUsdUfePath) + + return (ps, aXlateOp, aXlation, aUsdUfePathStr, aUsdUfePath, aUsdItem, + bXlateOp, bXlation, bUsdUfePathStr, bUsdUfePath, bUsdItem) From 4e837940180ea322776475498823c0542ed64fa2 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Wed, 27 Oct 2021 13:14:09 -0400 Subject: [PATCH 02/12] Clang format. --- lib/mayaUsd/fileio/jobs/readJob.cpp | 3 +- lib/mayaUsd/fileio/jobs/writeJob.cpp | 3 +- lib/mayaUsd/fileio/primUpdater.cpp | 59 ++-- lib/mayaUsd/fileio/primUpdater.h | 1 - lib/mayaUsd/fileio/primUpdaterArgs.cpp | 9 +- lib/mayaUsd/fileio/primUpdaterArgs.h | 9 +- lib/mayaUsd/fileio/primUpdaterContext.cpp | 7 +- lib/mayaUsd/fileio/primUpdaterContext.h | 26 +- lib/mayaUsd/fileio/primUpdaterManager.cpp | 331 +++++++++--------- lib/mayaUsd/fileio/primUpdaterManager.h | 7 +- lib/mayaUsd/python/wrapPrimUpdaterManager.cpp | 4 +- lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp | 3 +- lib/mayaUsd/ufe/UsdHierarchy.cpp | 3 +- lib/mayaUsd/ufe/UsdPathMappingHandler.cpp | 2 +- lib/mayaUsd/ufe/Utils.cpp | 2 +- lib/mayaUsd/utils/traverseLayer.cpp | 37 +- lib/mayaUsd/utils/traverseLayer.h | 15 +- lib/mayaUsd/utils/util.cpp | 2 +- 18 files changed, 256 insertions(+), 267 deletions(-) diff --git a/lib/mayaUsd/fileio/jobs/readJob.cpp b/lib/mayaUsd/fileio/jobs/readJob.cpp index 8f1df95592..2d70598a79 100644 --- a/lib/mayaUsd/fileio/jobs/readJob.cpp +++ b/lib/mayaUsd/fileio/jobs/readJob.cpp @@ -589,8 +589,7 @@ const MDagPath& UsdMaya_ReadJob::GetMayaRootDagPath() const { return mMayaRootDa double UsdMaya_ReadJob::timeSampleMultiplier() const { return mTimeSampleMultiplier; } -const UsdMayaPrimReaderContext::ObjectRegistry& -UsdMaya_ReadJob::GetNewNodeRegistry() const +const UsdMayaPrimReaderContext::ObjectRegistry& UsdMaya_ReadJob::GetNewNodeRegistry() const { return mNewNodeRegistry; } diff --git a/lib/mayaUsd/fileio/jobs/writeJob.cpp b/lib/mayaUsd/fileio/jobs/writeJob.cpp index 523fe6cebc..49e591c934 100644 --- a/lib/mayaUsd/fileio/jobs/writeJob.cpp +++ b/lib/mayaUsd/fileio/jobs/writeJob.cpp @@ -827,8 +827,7 @@ bool UsdMaya_WriteJob::_CheckNameClashes(const SdfPath& path, const MDagPath& da return true; } -const UsdMayaUtil::MDagPathMap& -UsdMaya_WriteJob::GetDagPathToUsdPathMap() const +const UsdMayaUtil::MDagPathMap& UsdMaya_WriteJob::GetDagPathToUsdPathMap() const { return mDagPathToUsdPathMap; } diff --git a/lib/mayaUsd/fileio/primUpdater.cpp b/lib/mayaUsd/fileio/primUpdater.cpp index fa9c795f9e..df2f7b7222 100644 --- a/lib/mayaUsd/fileio/primUpdater.cpp +++ b/lib/mayaUsd/fileio/primUpdater.cpp @@ -88,10 +88,7 @@ UsdMayaPrimUpdater::UsdMayaPrimUpdater(const MFnDependencyNode& depNodeFn, const { } -bool UsdMayaPrimUpdater::Pull(const UsdMayaPrimUpdaterContext& context) -{ - return true; -} +bool UsdMayaPrimUpdater::Pull(const UsdMayaPrimUpdaterContext& context) { return true; } bool UsdMayaPrimUpdater::DiscardEdits(const UsdMayaPrimUpdaterContext& context) { @@ -124,27 +121,42 @@ bool UsdMayaPrimUpdater::PushCopySpecs( // Since we're copying a single prim and not recursing to children, // topSrcPath and srcPath will be equal, and so will topDstPath and dstPath. auto shouldCopyValueFn = [topSrcPath, topDstPath]( - SdfSpecType specType, const TfToken& field, - const SdfLayerHandle& srcLayer, const SdfPath& srcPath, - bool fieldInSrc, - const SdfLayerHandle& dstLayer, const SdfPath& dstPath, - bool fieldInDst, - boost::optional* valueToCopy - ) { - return SdfShouldCopyValue(topSrcPath, topDstPath, specType, - field, srcLayer, srcPath, fieldInSrc, dstLayer, dstPath, - fieldInDst, valueToCopy); + SdfSpecType specType, + const TfToken& field, + const SdfLayerHandle& srcLayer, + const SdfPath& srcPath, + bool fieldInSrc, + const SdfLayerHandle& dstLayer, + const SdfPath& dstPath, + bool fieldInDst, + boost::optional* valueToCopy) { + return SdfShouldCopyValue( + topSrcPath, + topDstPath, + specType, + field, + srcLayer, + srcPath, + fieldInSrc, + dstLayer, + dstPath, + fieldInDst, + valueToCopy); }; - auto dontCopyChildrenFn = []( - const TfToken& childrenField, const SdfLayerHandle&, const SdfPath&, - bool, const SdfLayerHandle&, const SdfPath&, - bool, boost::optional*, boost::optional* - ) { + auto dontCopyChildrenFn = [](const TfToken& childrenField, + const SdfLayerHandle&, + const SdfPath&, + bool, + const SdfLayerHandle&, + const SdfPath&, + bool, + boost::optional*, + boost::optional*) { // There must be an existing list of static children fields. // PPT, 18-Oct-21. static TfToken properties("properties"); static TfToken primChildren("primChildren"); - + // See traverseLayer() implementation for full list of children field. // Property children must be copied, prim children must not. What // about other children field? If we understand the complete list, we @@ -152,8 +164,7 @@ bool UsdMayaPrimUpdater::PushCopySpecs( // 18-Oct-21. if (childrenField == properties) { return true; - } - else if (childrenField == primChildren) { + } else if (childrenField == primChildren) { return false; } @@ -161,8 +172,8 @@ bool UsdMayaPrimUpdater::PushCopySpecs( return true; }; - return SdfCopySpec(srcLayer, topSrcPath, dstLayer, topDstPath, - shouldCopyValueFn, dontCopyChildrenFn); + return SdfCopySpec( + srcLayer, topSrcPath, dstLayer, topDstPath, shouldCopyValueFn, dontCopyChildrenFn); } const MObject& UsdMayaPrimUpdater::GetMayaObject() const { return _mayaObject; } diff --git a/lib/mayaUsd/fileio/primUpdater.h b/lib/mayaUsd/fileio/primUpdater.h index ad2dc53a79..fe6a93eb23 100644 --- a/lib/mayaUsd/fileio/primUpdater.h +++ b/lib/mayaUsd/fileio/primUpdater.h @@ -28,7 +28,6 @@ #include #include #include - #include #include diff --git a/lib/mayaUsd/fileio/primUpdaterArgs.cpp b/lib/mayaUsd/fileio/primUpdaterArgs.cpp index dd10aeb942..33d900cbaa 100644 --- a/lib/mayaUsd/fileio/primUpdaterArgs.cpp +++ b/lib/mayaUsd/fileio/primUpdaterArgs.cpp @@ -33,15 +33,14 @@ static bool _Boolean(const VtDictionary& userArgs, const TfToken& key) } UsdMayaPrimUpdaterArgs::UsdMayaPrimUpdaterArgs(const VtDictionary& userArgs) - : _copyOperation(_Boolean(userArgs, UsdMayaPrimUpdaterArgsTokens->copyOperation)) + : _copyOperation(_Boolean(userArgs, UsdMayaPrimUpdaterArgsTokens->copyOperation)) { } /*static*/ UsdMayaPrimUpdaterArgs UsdMayaPrimUpdaterArgs::createFromDictionary(const VtDictionary& userArgs) { - return UsdMayaPrimUpdaterArgs( - VtDictionaryOver(userArgs, getDefaultDictionary())); + return UsdMayaPrimUpdaterArgs(VtDictionaryOver(userArgs, getDefaultDictionary())); } /*static*/ @@ -49,9 +48,7 @@ const VtDictionary& UsdMayaPrimUpdaterArgs::getDefaultDictionary() { static VtDictionary d; static std::once_flag once; - std::call_once(once, []() { - d[UsdMayaPrimUpdaterArgsTokens->copyOperation] = false; - }); + std::call_once(once, []() { d[UsdMayaPrimUpdaterArgsTokens->copyOperation] = false; }); return d; } diff --git a/lib/mayaUsd/fileio/primUpdaterArgs.h b/lib/mayaUsd/fileio/primUpdaterArgs.h index f20d5410c5..e3b8502a88 100644 --- a/lib/mayaUsd/fileio/primUpdaterArgs.h +++ b/lib/mayaUsd/fileio/primUpdaterArgs.h @@ -18,9 +18,9 @@ #include -#include #include #include +#include #include #include @@ -41,17 +41,16 @@ TF_DECLARE_PUBLIC_TOKENS( /// \brief Arguments that configure the updater. struct UsdMayaPrimUpdaterArgs { - const bool _copyOperation{false}; + const bool _copyOperation { false }; MAYAUSD_CORE_PUBLIC static UsdMayaPrimUpdaterArgs createFromDictionary(const VtDictionary& userArgs); - + MAYAUSD_CORE_PUBLIC static const VtDictionary& getDefaultDictionary(); - + private: UsdMayaPrimUpdaterArgs(const VtDictionary& userArgs); - }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/fileio/primUpdaterContext.cpp b/lib/mayaUsd/fileio/primUpdaterContext.cpp index 582150fc4b..f2f86e4d8b 100644 --- a/lib/mayaUsd/fileio/primUpdaterContext.cpp +++ b/lib/mayaUsd/fileio/primUpdaterContext.cpp @@ -22,8 +22,7 @@ UsdMayaPrimUpdaterContext::UsdMayaPrimUpdaterContext( const UsdTimeCode& timeCode, const UsdStageRefPtr& stage, const VtDictionary& userArgs, - const UsdPathToDagPathMapPtr& pathMap -) + const UsdPathToDagPathMapPtr& pathMap) : _timeCode(timeCode) , _stage(stage) , _pathMap(pathMap) @@ -32,9 +31,7 @@ UsdMayaPrimUpdaterContext::UsdMayaPrimUpdaterContext( { } -MDagPath UsdMayaPrimUpdaterContext::MapSdfPathToDagPath( - const SdfPath& sdfPath -) const +MDagPath UsdMayaPrimUpdaterContext::MapSdfPathToDagPath(const SdfPath& sdfPath) const { if (!_pathMap || sdfPath.IsEmpty()) { return MDagPath(); diff --git a/lib/mayaUsd/fileio/primUpdaterContext.h b/lib/mayaUsd/fileio/primUpdaterContext.h index c2bad47fba..37ac990657 100644 --- a/lib/mayaUsd/fileio/primUpdaterContext.h +++ b/lib/mayaUsd/fileio/primUpdaterContext.h @@ -18,17 +18,16 @@ #define PXRUSDMAYA_PRIMUPDATERCONTEXT_H #include - #include +#include #include #include #include -#include #include -#include // shared_ptr +#include // shared_ptr PXR_NAMESPACE_OPEN_SCOPE @@ -40,33 +39,38 @@ PXR_NAMESPACE_OPEN_SCOPE class UsdMayaPrimUpdaterContext { public: - using UsdPathToDagPathMap = TfHashMap; using UsdPathToDagPathMapPtr = std::shared_ptr; - + MAYAUSD_CORE_PUBLIC - UsdMayaPrimUpdaterContext(const UsdTimeCode& timeCode, const UsdStageRefPtr& stage, const VtDictionary& userArgs, const UsdPathToDagPathMapPtr& pathMap = nullptr); + UsdMayaPrimUpdaterContext( + const UsdTimeCode& timeCode, + const UsdStageRefPtr& stage, + const VtDictionary& userArgs, + const UsdPathToDagPathMapPtr& pathMap = nullptr); /// \brief returns the time frame where data should be edited. const UsdTimeCode& GetTimeCode() const { return _timeCode; } /// \brief returns the usd stage that is being written to. UsdStageRefPtr GetUsdStage() const { return _stage; } - - /// \brief Return dictionary with user defined arguments. Can contain a mix of reader/writer and updater args + + /// \brief Return dictionary with user defined arguments. Can contain a mix of reader/writer and + /// updater args const VtDictionary& GetUserArgs() const { return _userArgs; } - + /// \brief Return updater arguments const UsdMayaPrimUpdaterArgs& GetArgs() const { return _args; } - /// \brief Returns the Maya Dag path corresponding to a pulled USD path. The Dag path will be empty if no correspondence exists. + /// \brief Returns the Maya Dag path corresponding to a pulled USD path. The Dag path will be + /// empty if no correspondence exists. MDagPath MapSdfPathToDagPath(const SdfPath& sdfPath) const; private: const UsdTimeCode& _timeCode; const UsdStageRefPtr _stage; const UsdPathToDagPathMapPtr _pathMap; - + const VtDictionary& _userArgs; const UsdMayaPrimUpdaterArgs _args; }; diff --git a/lib/mayaUsd/fileio/primUpdaterManager.cpp b/lib/mayaUsd/fileio/primUpdaterManager.cpp index c27b47d46b..13491bf55a 100644 --- a/lib/mayaUsd/fileio/primUpdaterManager.cpp +++ b/lib/mayaUsd/fileio/primUpdaterManager.cpp @@ -15,16 +15,16 @@ // #include "primUpdaterManager.h" -#include #include -#include #include +#include #include #include +#include #include #include -#include #include +#include #include #include @@ -34,14 +34,13 @@ #include #include -#include #include -#include -#include #include -#include #include - +#include +#include +#include +#include #include #include #include @@ -52,10 +51,11 @@ // Allow for use of MObjectHandle with std::unordered_map. namespace std { -template <> struct hash { - std::size_t operator()(const MObjectHandle& obj) const {return obj.hashCode();} +template <> struct hash +{ + std::size_t operator()(const MObjectHandle& obj) const { return obj.hashCode(); } }; -} +} // namespace std namespace MAYAUSD_NS_DEF { namespace ufe { @@ -103,8 +103,7 @@ Ufe::Path usdToMaya(const Ufe::Path& usdPath) return Ufe::Path(); } std::string dagPathStr; - if (!TF_VERIFY(PXR_NS::UsdMayaPrimUpdater::readPullInformation( - prim, dagPathStr))) { + if (!TF_VERIFY(PXR_NS::UsdMayaPrimUpdater::readPullInformation(prim, dagPathStr))) { return Ufe::Path(); } @@ -137,13 +136,13 @@ void select(const MDagPath& dagPath) // The UFE path and the prim refer to the same object: the prim is passed in as // an optimization to avoid an additional call to ufePathToPrim(). bool writePullInformation( - const Ufe::Path& ufePulledPath, - const PXR_NS::UsdPrim& pulledPrim, - const MDagPath& path) + const Ufe::Path& ufePulledPath, + const PXR_NS::UsdPrim& pulledPrim, + const MDagPath& path) { // Add to a set MObject pullSetObj; - auto status = UsdMayaUtil::GetMObjectByName(kPullSetName, pullSetObj); + auto status = UsdMayaUtil::GetMObjectByName(kPullSetName, pullSetObj); if (status != MStatus::kSuccess) { MFnSet fnSet; MSelectionList selList; @@ -168,8 +167,8 @@ bool writePullInformation( MObject strAttrObject = fnStringData.create(""); MFnTypedAttribute attr; - MObject attrObj = attr.create(kPullDGMetadataKey, - kPullDGMetadataKey, MFnData::kString, strAttrObject); + MObject attrObj + = attr.create(kPullDGMetadataKey, kPullDGMetadataKey, MFnData::kString, strAttrObject); status = depNode.addAttribute(attrObj); dgMetadata = depNode.findPlug(kPullDGMetadataKey, &status); if (status != MStatus::kSuccess) { @@ -193,8 +192,8 @@ void removePullInformation(const Ufe::Path& ufePulledPath) // bool addExcludeFromRendering(const Ufe::Path& ufePulledPath) { - auto proxyShape = MayaUsd::ufe::getProxyShape(ufePulledPath); - MStatus status; + auto proxyShape = MayaUsd::ufe::getProxyShape(ufePulledPath); + MStatus status; MFnDependencyNode depNode(proxyShape->thisMObject(), &status); CHECK_MSTATUS_AND_RETURN(status, false); @@ -206,7 +205,7 @@ bool addExcludeFromRendering(const Ufe::Path& ufePulledPath) status = excludePrimPathsPlug.getValue(excludePrimPathsStr); std::vector excludePrimPaths = TfStringTokenize(excludePrimPathsStr.asChar(), ","); - std::string sdfPathStr = ufeToSdfPath(ufePulledPath).GetText(); + std::string sdfPathStr = ufeToSdfPath(ufePulledPath).GetText(); if (std::find(excludePrimPaths.begin(), excludePrimPaths.end(), sdfPathStr) != excludePrimPaths.end()) return true; @@ -222,11 +221,11 @@ bool addExcludeFromRendering(const Ufe::Path& ufePulledPath) // bool removeExcludeFromRendering(const Ufe::Path& ufePulledPath) { - auto proxyShape = MayaUsd::ufe::getProxyShape(ufePulledPath); - auto prim = MayaUsd::ufe::ufePathToPrim(ufePulledPath); + auto proxyShape = MayaUsd::ufe::getProxyShape(ufePulledPath); + auto prim = MayaUsd::ufe::ufePathToPrim(ufePulledPath); std::string sdfPathStr = prim.GetPath().GetText(); - MStatus status; + MStatus status; MFnDependencyNode depNode(proxyShape->thisMObject(), &status); CHECK_MSTATUS_AND_RETURN(status, false); @@ -258,10 +257,9 @@ using PullImportPaths = std::pair, std::vector> PullImportPaths PullImport( const Ufe::Path& ufePulledPath, const UsdPrim& pulledPrim, - const UsdMayaPrimUpdaterContext& context -) + const UsdMayaPrimUpdaterContext& context) { - std::vector addedDagPaths; + std::vector addedDagPaths; std::vector pulledUfePaths; std::string mFileName = context.GetUsdStage()->GetRootLayer()->GetIdentifier(); @@ -329,13 +327,12 @@ PullImportPaths PullImport( // Invert the new node registry, for MObject to Ufe::Path lookup. using ObjToUfePath = std::unordered_map; ObjToUfePath objToUfePath; - const auto& ps = ufePulledPath.getSegments()[0]; - const auto rtid = MayaUsd::ufe::getUsdRunTimeId(); + const auto& ps = ufePulledPath.getSegments()[0]; + const auto rtid = MayaUsd::ufe::getUsdRunTimeId(); for (const auto& v : readJob.GetNewNodeRegistry()) { - Ufe::Path::Segments s{ps, Ufe::PathSegment(v.first, rtid, '/')}; - Ufe::Path p(std::move(s)); - objToUfePath.insert(ObjToUfePath::value_type( - MObjectHandle(v.second), p)); + Ufe::Path::Segments s { ps, Ufe::PathSegment(v.first, rtid, '/') }; + Ufe::Path p(std::move(s)); + objToUfePath.insert(ObjToUfePath::value_type(MObjectHandle(v.second), p)); } // For each added Dag path, get the UFE path of the pulled USD prim. @@ -346,25 +343,22 @@ PullImportPaths PullImport( pulledUfePaths.emplace_back(found->second); } - return PullImportPaths(addedDagPaths, pulledUfePaths); + return PullImportPaths(addedDagPaths, pulledUfePaths); } //------------------------------------------------------------------------------ // // Perform the customization step of the pull (second step). -bool PullCustomize( - const PullImportPaths& importedPaths, - const UsdMayaPrimUpdaterContext& context -) +bool PullCustomize(const PullImportPaths& importedPaths, const UsdMayaPrimUpdaterContext& context) { TF_AXIOM(importedPaths.first.size() == importedPaths.second.size()); auto dagPathIt = importedPaths.first.begin(); auto ufePathIt = importedPaths.second.begin(); for (; dagPathIt != importedPaths.first.end(); ++dagPathIt, ++ufePathIt) { - const auto& dagPath = *dagPathIt; - const auto& pulledUfePath = *ufePathIt; + const auto& dagPath = *dagPathIt; + const auto& pulledUfePath = *ufePathIt; MFnDependencyNode dgNodeFn(dagPath.node()); - + const std::string mayaTypeName(dgNodeFn.typeName().asChar()); auto registryItem = UsdMayaPrimUpdaterRegistry::FindOrFallback(mayaTypeName); @@ -389,19 +383,17 @@ bool PullCustomize( // SdfPath will be empty on error. using UsdPathToDagPathMap = TfHashMap; using UsdPathToDagPathMapPtr = std::shared_ptr; -using PushCustomizeSrc = - std::tuple; +using PushCustomizeSrc = std::tuple; PushCustomizeSrc PushExport( const Ufe::Path& ufePulledPath, const MObject& mayaObject, - const UsdMayaPrimUpdaterContext& context -) + const UsdMayaPrimUpdaterContext& context) { - UsdStageRefPtr srcStage = UsdStage::CreateInMemory(); - SdfLayerRefPtr srcLayer = srcStage->GetRootLayer(); + UsdStageRefPtr srcStage = UsdStage::CreateInMemory(); + SdfLayerRefPtr srcLayer = srcStage->GetRootLayer(); UsdPathToDagPathMapPtr pathMapPtr; - auto pushCustomizeSrc = std::make_tuple(SdfPath(), srcLayer, pathMapPtr); + auto pushCustomizeSrc = std::make_tuple(SdfPath(), srcLayer, pathMapPtr); // Copy to be able to add the export root. VtDictionary userArgs = context.GetUserArgs(); @@ -415,8 +407,9 @@ PushCustomizeSrc PushExport( UsdMayaUtil::MDagPathSet dagPaths; dagPaths.insert(dagPath); - GfInterval timeInterval = PXR_NS::UsdMayaPrimUpdater::isAnimated(dagPath) ? - GfInterval(MAnimControl::minTime().value(), MAnimControl::maxTime().value()) : GfInterval(); + GfInterval timeInterval = PXR_NS::UsdMayaPrimUpdater::isAnimated(dagPath) + ? GfInterval(MAnimControl::minTime().value(), MAnimControl::maxTime().value()) + : GfInterval(); double frameStride = 1.0; std::set frameSamples; @@ -441,8 +434,7 @@ PushCustomizeSrc PushExport( // Invert the Dag path to USD path map, to return it for prim updater use. auto usdPathToDagPathMap = std::make_shared(); for (const auto& v : writeJob.GetDagPathToUsdPathMap()) { - usdPathToDagPathMap->insert(UsdPathToDagPathMap::value_type( - v.second, v.first)); + usdPathToDagPathMap->insert(UsdPathToDagPathMap::value_type(v.second, v.first)); } std::get(pushCustomizeSrc) = usdPathToDagPathMap; @@ -459,14 +451,10 @@ PushCustomizeSrc PushExport( return pushCustomizeSrc; } - + //------------------------------------------------------------------------------ // -SdfPath getDstSdfPath( - const Ufe::Path& ufePulledPath, - const SdfPath& srcSdfPath, - bool isCopy -) +SdfPath getDstSdfPath(const Ufe::Path& ufePulledPath, const SdfPath& srcSdfPath, bool isCopy) { // If we got the destination path, extract it, otherwise use src path as // the destination. @@ -490,8 +478,7 @@ SdfPath getDstSdfPath( UsdMayaPrimUpdaterSharedPtr createUpdater( const SdfLayerRefPtr& srcLayer, const SdfPath& primSpecPath, - const UsdMayaPrimUpdaterContext& context -) + const UsdMayaPrimUpdaterContext& context) { using UpdaterFactoryFn = UsdMayaPrimUpdaterRegistry::UpdaterFactoryFn; @@ -502,24 +489,23 @@ UsdMayaPrimUpdaterSharedPtr createUpdater( } TfToken typeName = primSpec->GetTypeName(); - auto regItem = UsdMayaPrimUpdaterRegistry::FindOrFallback(typeName); - auto factory = std::get(regItem); + auto regItem = UsdMayaPrimUpdaterRegistry::FindOrFallback(typeName); + auto factory = std::get(regItem); // Create the UFE path corresponding to the primSpecPath, as required // by the prim updater factory. - auto psPath = MayaUsd::ufe::stagePath(context.GetUsdStage()); - Ufe::Path::Segments segments{psPath.getSegments()[0], - MayaUsd::ufe::usdPathToUfePathSegment(primSpecPath)}; - Ufe::Path ufePath(std::move(segments)); + auto psPath = MayaUsd::ufe::stagePath(context.GetUsdStage()); + Ufe::Path::Segments segments { psPath.getSegments()[0], + MayaUsd::ufe::usdPathToUfePathSegment(primSpecPath) }; + Ufe::Path ufePath(std::move(segments)); // Get the Maya object corresponding to the SdfPath. As of 19-Oct-2021, // the export write job only registers Maya Dag path to SdfPath // correspondence, so prims that correspond to Maya DG nodes (e.g. material // networks) don't have a corresponding Dag path. The prim updater // receives a null MObject in this case. - auto mayaDagPath = context.MapSdfPathToDagPath(primSpecPath); - MFnDependencyNode depNodeFn( - mayaDagPath.isValid() ? mayaDagPath.node() : MObject()); + auto mayaDagPath = context.MapSdfPathToDagPath(primSpecPath); + MFnDependencyNode depNodeFn(mayaDagPath.isValid() ? mayaDagPath.node() : MObject()); return factory(depNodeFn, ufePath); } @@ -532,56 +518,55 @@ UsdMayaPrimUpdaterSharedPtr createUpdater( bool PushCustomize( const Ufe::Path& ufePulledPath, const PushCustomizeSrc& src, - const UsdMayaPrimUpdaterContext& context -) + const UsdMayaPrimUpdaterContext& context) { const auto& srcRootPath = std::get(src); - const auto& srcLayer = std::get(src); + const auto& srcLayer = std::get(src); if (srcRootPath.IsEmpty()) { return false; } - const bool isCopy = context.GetArgs()._copyOperation; + const bool isCopy = context.GetArgs()._copyOperation; const auto& editTarget = context.GetUsdStage()->GetEditTarget(); - auto dstRootPath = editTarget.MapToSpecPath( - getDstSdfPath(ufePulledPath, srcRootPath, isCopy)); - auto dstRootParentPath = dstRootPath.GetParentPath(); - const auto& dstLayer = editTarget.GetLayer(); + auto dstRootPath = editTarget.MapToSpecPath(getDstSdfPath(ufePulledPath, srcRootPath, isCopy)); + auto dstRootParentPath = dstRootPath.GetParentPath(); + const auto& dstLayer = editTarget.GetLayer(); // Traverse the layer, creating a prim updater for each primSpec // along the way, and call PushCopySpec on the prim. using UpdaterFactoryFn = UsdMayaPrimUpdaterRegistry::UpdaterFactoryFn; - auto pushCopySpecsFn = [&context, srcLayer, dstLayer, dstRootParentPath]( - const SdfPath& srcPath - ) { - // We can be called with a primSpec path that is not a prim path - // (e.g. a property path like "/A.xformOp:translate"). This is not an - // error, just prune the traversal. FIXME Is this still true? We - // should not be traversing property specs. PPT, 20-Oct-2021. - if (!srcPath.IsPrimPath()) { - return false; - } - - auto updater = createUpdater(srcLayer, srcPath, context); - // If we cannot find an updater for the srcPath, prune the traversal. - if (!updater) { - TF_WARN("Could not create a prim updater for path %s during PushCopySpecs traversal, pruning at that point.", srcPath.GetText()); - return false; - } - auto relativeSrcPath = srcPath.MakeRelativePath(SdfPath::AbsoluteRootPath()); - auto dstPath = dstRootParentPath.AppendPath(relativeSrcPath); - - // Report PushCopySpecs() failure. - if (!updater->PushCopySpecs(srcLayer, srcPath, dstLayer, dstPath)) { - throw MayaUsd::TraversalFailure( - std::string("PushCopySpecs() failed."), srcPath); - } + auto pushCopySpecsFn + = [&context, srcLayer, dstLayer, dstRootParentPath](const SdfPath& srcPath) { + // We can be called with a primSpec path that is not a prim path + // (e.g. a property path like "/A.xformOp:translate"). This is not an + // error, just prune the traversal. FIXME Is this still true? We + // should not be traversing property specs. PPT, 20-Oct-2021. + if (!srcPath.IsPrimPath()) { + return false; + } + + auto updater = createUpdater(srcLayer, srcPath, context); + // If we cannot find an updater for the srcPath, prune the traversal. + if (!updater) { + TF_WARN( + "Could not create a prim updater for path %s during PushCopySpecs traversal, " + "pruning at that point.", + srcPath.GetText()); + return false; + } + auto relativeSrcPath = srcPath.MakeRelativePath(SdfPath::AbsoluteRootPath()); + auto dstPath = dstRootParentPath.AppendPath(relativeSrcPath); + + // Report PushCopySpecs() failure. + if (!updater->PushCopySpecs(srcLayer, srcPath, dstLayer, dstPath)) { + throw MayaUsd::TraversalFailure(std::string("PushCopySpecs() failed."), srcPath); + } + + // Continue normal traversal without pruning. + return true; + }; - // Continue normal traversal without pruning. - return true; - }; - if (!MayaUsd::traverseLayer(srcLayer, srcRootPath, pushCopySpecsFn)) { return false; } @@ -597,7 +582,7 @@ bool PushCustomize( // SdfLayer::TraversalFn does not return a status, so must report // failure through an exception. auto pushEndFn = [&context, srcLayer](const SdfPath& primSpecPath) { - // We can be called with a primSpec path that is not a prim path + // We can be called with a primSpec path that is not a prim path // (e.g. a property path like "/A.xformOp:translate"). This is not an // error, just a no-op. if (!primSpecPath.IsPrimPath()) { @@ -606,25 +591,28 @@ bool PushCustomize( auto updater = createUpdater(srcLayer, primSpecPath, context); if (!updater) { - TF_WARN("Could not create a prim updater for path %s during PushEnd() traversal, pruning at that point.", primSpecPath.GetText()); + TF_WARN( + "Could not create a prim updater for path %s during PushEnd() traversal, pruning " + "at that point.", + primSpecPath.GetText()); return; } - + // Report PushEnd() failure. if (!updater->PushEnd(context)) { - throw MayaUsd::TraversalFailure( - std::string("PushEnd() failed."), primSpecPath); + throw MayaUsd::TraversalFailure(std::string("PushEnd() failed."), primSpecPath); } }; - + // SdfLayer::Traverse does not return a status, so must report failure // through an exception. try { srcLayer->Traverse(srcRootPath, pushEndFn); - } - catch (const MayaUsd::TraversalFailure& e) { - TF_WARN("PushEnd() layer traversal failed for path %s: %s", - e.path().GetAsString(), e.reason().c_str()); + } catch (const MayaUsd::TraversalFailure& e) { + TF_WARN( + "PushEnd() layer traversal failed for path %s: %s", + e.path().GetAsString(), + e.reason().c_str()); return false; } @@ -666,8 +654,8 @@ PrimUpdaterManager::PrimUpdaterManager() _cbIds.append( MSceneMessage::addCallback(MSceneMessage::kBeforeNew, beforeNewOrOpenCallback, this, &res)); CHECK_MSTATUS(res); - _cbIds.append( - MSceneMessage::addCallback(MSceneMessage::kBeforeOpen, beforeNewOrOpenCallback, this, &res)); + _cbIds.append(MSceneMessage::addCallback( + MSceneMessage::kBeforeOpen, beforeNewOrOpenCallback, this, &res)); CHECK_MSTATUS(res); TfSingleton::SetInstanceConstructed(*this); @@ -698,10 +686,10 @@ bool PrimUpdaterManager::Push(const MFnDependencyNode& depNodeFn, const Ufe::Pat PushPullScope scopeIt(_inPushPull); VtDictionary exportArgs = UsdMayaJobExportArgs::GetDefaultDictionary(); - auto updaterArgs = UsdMayaPrimUpdaterArgs::createFromDictionary(exportArgs); - auto mayaPath = usdToMaya(pulledPath); - MDagPath pullParentPath; - const bool isCopy = updaterArgs._copyOperation; + auto updaterArgs = UsdMayaPrimUpdaterArgs::createFromDictionary(exportArgs); + auto mayaPath = usdToMaya(pulledPath); + MDagPath pullParentPath; + const bool isCopy = updaterArgs._copyOperation; if (!isCopy) { // The pull parent is simply the parent of the pulled path. pullParentPath = MayaUsd::ufe::ufeToDagPath(mayaPath.pop()); @@ -711,10 +699,10 @@ bool PrimUpdaterManager::Push(const MFnDependencyNode& depNodeFn, const Ufe::Pat lockNodes(pullParentPath, false); } - UsdStageRefPtr proxyStage = proxyShape->usdPrim().GetStage(); + UsdStageRefPtr proxyStage = proxyShape->usdPrim().GetStage(); UsdMayaPrimUpdaterContext context(proxyShape->getTime(), proxyStage, exportArgs); - auto ufeMayaItem = Ufe::Hierarchy::createItem(mayaPath); + auto ufeMayaItem = Ufe::Hierarchy::createItem(mayaPath); auto& scene = Ufe::Scene::instance(); if (!isCopy && TF_VERIFY(ufeMayaItem)) scene.notify(Ufe::ObjectPreDelete(ufeMayaItem)); @@ -730,7 +718,11 @@ bool PrimUpdaterManager::Push(const MFnDependencyNode& depNodeFn, const Ufe::Pat // 2) Traverse the in-memory layer, creating a prim updater for each prim, // and call Push for each updater. Build a new context with the USD path // to Maya path mapping information. - UsdMayaPrimUpdaterContext customizeContext(proxyShape->getTime(), proxyStage, exportArgs, std::get(pushCustomizeSrc)); + UsdMayaPrimUpdaterContext customizeContext( + proxyShape->getTime(), + proxyStage, + exportArgs, + std::get(pushCustomizeSrc)); if (!PushCustomize(pulledPath, pushCustomizeSrc, customizeContext)) { return false; @@ -766,18 +758,18 @@ bool PrimUpdaterManager::Pull(const Ufe::Path& path) PushPullScope scopeIt(_inPushPull); VtDictionary importArgs = UsdMayaJobImportArgs::GetDefaultDictionary(); - auto updaterArgs = UsdMayaPrimUpdaterArgs::createFromDictionary(importArgs); + auto updaterArgs = UsdMayaPrimUpdaterArgs::createFromDictionary(importArgs); MDagPath pullParentPath; - if (!updaterArgs._copyOperation && - !(pullParentPath = SetupPullParent(path, importArgs)).isValid()) { + if (!updaterArgs._copyOperation + && !(pullParentPath = SetupPullParent(path, importArgs)).isValid()) { return false; } UsdMayaPrimUpdaterContext context(proxyShape->getTime(), pulledPrim.GetStage(), importArgs); auto& scene = Ufe::Scene::instance(); - auto ufeItem = Ufe::Hierarchy::createItem(path); + auto ufeItem = Ufe::Hierarchy::createItem(path); if (!updaterArgs._copyOperation && TF_VERIFY(ufeItem)) scene.notify(Ufe::ObjectPreDelete(ufeItem)); @@ -818,11 +810,12 @@ bool PrimUpdaterManager::PullClear(const Ufe::Path& pulledPath) auto mayaPath = usdToMaya(pulledPath); auto mayaDagPath = MayaUsd::ufe::ufeToDagPath(mayaPath); - - VtDictionary userArgs; - UsdMayaPrimUpdaterContext context(proxyShape->getTime(), proxyShape->usdPrim().GetStage(), userArgs); - auto ufeMayaItem = Ufe::Hierarchy::createItem(mayaPath); + VtDictionary userArgs; + UsdMayaPrimUpdaterContext context( + proxyShape->getTime(), proxyShape->usdPrim().GetStage(), userArgs); + + auto ufeMayaItem = Ufe::Hierarchy::createItem(mayaPath); auto& scene = Ufe::Scene::instance(); if (TF_VERIFY(ufeMayaItem)) scene.notify(Ufe::ObjectPreDelete(ufeMayaItem)); @@ -864,17 +857,17 @@ bool PrimUpdaterManager::CopyBetween(const Ufe::Path& srcPath, const Ufe::Path& { MayaUsdProxyShapeBase* srcProxyShape = MayaUsd::ufe::getProxyShape(srcPath); MayaUsdProxyShapeBase* dstProxyShape = MayaUsd::ufe::getProxyShape(dstPath); - + PushPullScope scopeIt(_inPushPull); - bool ret = false; - + bool ret = false; + // Copy from USD to DG if (srcProxyShape && dstProxyShape == nullptr) { SdfPath srcSdfPath = ufeToSdfPath(srcPath); if (srcSdfPath.IsEmpty()) { return false; } - + MFnDependencyNode depNodeFn; UsdStageRefPtr proxyStage = srcProxyShape->usdPrim().GetStage(); @@ -884,25 +877,26 @@ bool PrimUpdaterManager::CopyBetween(const Ufe::Path& srcPath, const Ufe::Path& auto registryItem = UsdMayaPrimUpdaterRegistry::FindOrFallback(typeName); auto factory = std::get(registryItem); UsdMayaPrimUpdaterSharedPtr updater = factory(depNodeFn, srcPath); - + VtDictionary userArgs = UsdMayaJobImportArgs::GetDefaultDictionary(); // We will only do copy between two data models, setting this in arguments // to configure the updater userArgs[UsdMayaPrimUpdaterArgsTokens->copyOperation] = true; - + UsdMayaPrimUpdaterContext context(srcProxyShape->getTime(), proxyStage, userArgs); - + // FIXME Re-implement with proper traversal. PPT, 22-Oct-2021. #if 0 ret = updater->Pull(context); #endif } // Copy from DG to USD - else if(srcProxyShape == nullptr && dstProxyShape) { + else if (srcProxyShape == nullptr && dstProxyShape) { // Remove the leading "|world| component. - MDagPath dagPath = PXR_NS::UsdMayaUtil::nameToDagPath(srcPath.getSegments()[0].popHead().string()); + MDagPath dagPath + = PXR_NS::UsdMayaUtil::nameToDagPath(srcPath.getSegments()[0].popHead().string()); MFnDependencyNode dgNodeFn(dagPath.node()); - + const std::string mayaTypeName(dgNodeFn.typeName().asChar()); auto registryItem = UsdMayaPrimUpdaterRegistry::FindOrFallback(mayaTypeName); @@ -910,20 +904,20 @@ bool PrimUpdaterManager::CopyBetween(const Ufe::Path& srcPath, const Ufe::Path& UsdMayaPrimUpdaterSharedPtr updater = factory(dgNodeFn, dstPath); VtDictionary userArgs = UsdMayaJobExportArgs::GetDefaultDictionary(); - + // We will only do copy between two data models, setting this in arguments // to configure the updater userArgs[UsdMayaPrimUpdaterArgsTokens->copyOperation] = true; - - UsdMayaPrimUpdaterContext context(dstProxyShape->getTime(), dstProxyShape->usdPrim().GetStage(), userArgs); - + + UsdMayaPrimUpdaterContext context( + dstProxyShape->getTime(), dstProxyShape->usdPrim().GetStage(), userArgs); + // FIXME Re-implement with proper traversal. PPT, 22-Oct-2021. #if 0 ret = updater->Push(context); #endif - - if(ret) - { + + if (ret) { auto ufeItem = Ufe::Hierarchy::createItem(dstPath); if (TF_VERIFY(ufeItem)) { Ufe::Scene::instance().notify(Ufe::SubtreeInvalidate(ufeItem)); @@ -934,7 +928,7 @@ bool PrimUpdaterManager::CopyBetween(const Ufe::Path& srcPath, const Ufe::Path& else { return false; } - + return ret; } @@ -993,11 +987,11 @@ bool PrimUpdaterManager::FindOrCreatePullRoot() } // No saved pull root. Try to find one in the scene, and save its MObject. - auto worldObj = MItDag().root(); + auto worldObj = MItDag().root(); MFnDagNode world(worldObj); - auto nbWorldChildren = world.childCount(); + auto nbWorldChildren = world.childCount(); for (unsigned int i = 0; i < nbWorldChildren; ++i) { - auto childObj = world.child(i); + auto childObj = world.child(i); MFnDependencyNode child(childObj); if (child.name() == pullRootName) { _pullRoot = childObj; @@ -1007,9 +1001,8 @@ bool PrimUpdaterManager::FindOrCreatePullRoot() // No pull root in the scene, so create one. MDagModifier dagMod; - MStatus status; - MObject pullRootObj = dagMod.createNode( - MString("transform"), MObject::kNullObj, &status); + MStatus status; + MObject pullRootObj = dagMod.createNode(MString("transform"), MObject::kNullObj, &status); if (status != MStatus::kSuccess) { return false; } @@ -1034,19 +1027,17 @@ bool PrimUpdaterManager::FindOrCreatePullRoot() MObject PrimUpdaterManager::CreatePullParent(const Ufe::Path& pulledPath) { MDagModifier dagMod; - MStatus status; - MObject pullParentObj = dagMod.createNode( - MString("transform"), _pullRoot, &status); + MStatus status; + MObject pullParentObj = dagMod.createNode(MString("transform"), _pullRoot, &status); if (status != MStatus::kSuccess) { return MObject::kNullObj; } // Rename the pull parent to be the name of the node plus a "Parent" suffix. - status = dagMod.renameNode(pullParentObj, - MString(pulledPath.back().string().c_str()) + MString("Parent")); + status = dagMod.renameNode( + pullParentObj, MString(pulledPath.back().string().c_str()) + MString("Parent")); - return (dagMod.doIt() == MStatus::kSuccess) ? - pullParentObj : MObject::kNullObj; + return (dagMod.doIt() == MStatus::kSuccess) ? pullParentObj : MObject::kNullObj; } bool PrimUpdaterManager::RemovePullParent(const MDagPath& parentDagPath) @@ -1063,7 +1054,7 @@ bool PrimUpdaterManager::RemovePullParent(const MDagPath& parentDagPath) // If the pull parent was the last child of the pull root, remove the pull // root as well, and null out our pull root cache. MFnDagNode pullRoot(_pullRoot); - auto nbPullRootChildren = pullRoot.childCount(); + auto nbPullRootChildren = pullRoot.childCount(); if (nbPullRootChildren == 1) { if (dgMod.deleteNode(_pullRoot) != MStatus::kSuccess) { return false; @@ -1074,10 +1065,7 @@ bool PrimUpdaterManager::RemovePullParent(const MDagPath& parentDagPath) return dgMod.doIt() == MStatus::kSuccess; } -MDagPath PrimUpdaterManager::SetupPullParent( - const Ufe::Path& pulledPath, - VtDictionary& args -) +MDagPath PrimUpdaterManager::SetupPullParent(const Ufe::Path& pulledPath, VtDictionary& args) { if (!FindOrCreatePullRoot()) { return MDagPath(); @@ -1095,8 +1083,7 @@ MDagPath PrimUpdaterManager::SetupPullParent( } // Add pull parent path to import args as a string. - args[kPullParentPathKey] = - VtValue(std::string(pullParentPath.fullPathName().asChar())); + args[kPullParentPathKey] = VtValue(std::string(pullParentPath.fullPathName().asChar())); return pullParentPath; } diff --git a/lib/mayaUsd/fileio/primUpdaterManager.h b/lib/mayaUsd/fileio/primUpdaterManager.h index e57334b50a..ea0c1a664d 100644 --- a/lib/mayaUsd/fileio/primUpdaterManager.h +++ b/lib/mayaUsd/fileio/primUpdaterManager.h @@ -25,11 +25,10 @@ #include #include +#include #include #include #include -#include - #include PXR_NAMESPACE_OPEN_SCOPE @@ -48,7 +47,7 @@ class PrimUpdaterManager : public PXR_NS::TfWeakBase MAYAUSD_CORE_PUBLIC bool PullClear(const Ufe::Path& path); - + MAYAUSD_CORE_PUBLIC bool CopyBetween(const Ufe::Path& srcPath, const Ufe::Path& dstPath); @@ -83,7 +82,7 @@ class PrimUpdaterManager : public PXR_NS::TfWeakBase bool _inPushPull { false }; // Initialize pull root MObject to null object. - MObject _pullRoot{}; + MObject _pullRoot {}; // Reset pull root on file new / file open. MCallbackIdArray _cbIds; diff --git a/lib/mayaUsd/python/wrapPrimUpdaterManager.cpp b/lib/mayaUsd/python/wrapPrimUpdaterManager.cpp index ccb30be142..b387d144cb 100644 --- a/lib/mayaUsd/python/wrapPrimUpdaterManager.cpp +++ b/lib/mayaUsd/python/wrapPrimUpdaterManager.cpp @@ -80,9 +80,9 @@ bool copyBetween(const std::string& srcUfePathString, const std::string& dstUfeP Ufe::Path src = Ufe::PathString::path(srcUfePathString); Ufe::Path dst = Ufe::PathString::path(dstUfePathString); - if(src.empty() || dst.empty()) + if (src.empty() || dst.empty()) return false; - + return PrimUpdaterManager::GetInstance().CopyBetween(src, dst); } diff --git a/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp b/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp index 8d103c02f9..c5558955ed 100644 --- a/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp +++ b/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp @@ -36,7 +36,8 @@ #ifdef UFE_V3_FEATURES_AVAILABLE #include -#include // In UFE v2 but only needed for primUpdater. + +#include // In UFE v2 but only needed for primUpdater. #endif PXR_NAMESPACE_USING_DIRECTIVE diff --git a/lib/mayaUsd/ufe/UsdHierarchy.cpp b/lib/mayaUsd/ufe/UsdHierarchy.cpp index e8a65c4dea..b9c6cb520f 100644 --- a/lib/mayaUsd/ufe/UsdHierarchy.cpp +++ b/lib/mayaUsd/ufe/UsdHierarchy.cpp @@ -47,7 +47,8 @@ #ifdef UFE_V3_FEATURES_AVAILABLE #include #include -#include // In UFE v2 but only needed for primUpdater. + +#include // In UFE v2 but only needed for primUpdater. #endif PXR_NAMESPACE_USING_DIRECTIVE diff --git a/lib/mayaUsd/ufe/UsdPathMappingHandler.cpp b/lib/mayaUsd/ufe/UsdPathMappingHandler.cpp index 43f5b56661..d9a88e3a26 100644 --- a/lib/mayaUsd/ufe/UsdPathMappingHandler.cpp +++ b/lib/mayaUsd/ufe/UsdPathMappingHandler.cpp @@ -18,8 +18,8 @@ #include "UsdSceneItem.h" #include -#include #include +#include #include diff --git a/lib/mayaUsd/ufe/Utils.cpp b/lib/mayaUsd/ufe/Utils.cpp index af3ae66ddf..f80922cb94 100644 --- a/lib/mayaUsd/ufe/Utils.cpp +++ b/lib/mayaUsd/ufe/Utils.cpp @@ -360,7 +360,7 @@ MDagPath ufeToDagPath(const Ufe::Path& ufePath) #else ufePath.getSegments().size() #endif - > 1) { + > 1) { return MDagPath(); } return UsdMayaUtil::nameToDagPath( diff --git a/lib/mayaUsd/utils/traverseLayer.cpp b/lib/mayaUsd/utils/traverseLayer.cpp index 1ff9677bb3..c3ae67249f 100644 --- a/lib/mayaUsd/utils/traverseLayer.cpp +++ b/lib/mayaUsd/utils/traverseLayer.cpp @@ -26,31 +26,25 @@ PXR_NAMESPACE_USING_DIRECTIVE void _traverseLayer( const SdfLayerHandle& layer, const SdfPath& path, - const MayaUsd::TraverseLayerFn& fn -); + const MayaUsd::TraverseLayerFn& fn); -template -void -TraverseChildren( +template +void TraverseChildren( const SdfLayerHandle& layer, const SdfPath& path, - const MayaUsd::TraverseLayerFn& fn -) + const MayaUsd::TraverseLayerFn& fn) { - std::vector children = - layer->GetFieldAs >( + std::vector children + = layer->GetFieldAs>( path, ChildPolicy::GetChildrenToken(path)); - TF_FOR_ALL(i, children) { - _traverseLayer(layer, ChildPolicy::GetChildPath(path, *i), fn); - } + TF_FOR_ALL(i, children) { _traverseLayer(layer, ChildPolicy::GetChildPath(path, *i), fn); } } void _traverseLayer( const SdfLayerHandle& layer, const SdfPath& path, - const MayaUsd::TraverseLayerFn& fn -) + const MayaUsd::TraverseLayerFn& fn) { if (!fn(path)) { // Prune the traversal as requested by fn. @@ -66,7 +60,8 @@ void _traverseLayer( auto fields = primSpec->ListFields(); - TF_FOR_ALL(i, fields) { + TF_FOR_ALL(i, fields) + { if (*i == SdfChildrenKeys->PrimChildren) { TraverseChildren(layer, path, fn); } else if (*i == SdfChildrenKeys->PropertyChildren) { @@ -89,22 +84,20 @@ void _traverseLayer( } } -} +} // namespace namespace MAYAUSD_NS_DEF { bool traverseLayer( const PXR_NS::SdfLayerHandle& layer, const PXR_NS::SdfPath& path, - const TraverseLayerFn& fn -) + const TraverseLayerFn& fn) { try { _traverseLayer(layer, path, fn); - } - catch (const TraversalFailure& e) { - TF_WARN("Layer traversal failed for path %s: %s", - e.path().GetAsString(), e.reason().c_str()); + } catch (const TraversalFailure& e) { + TF_WARN( + "Layer traversal failed for path %s: %s", e.path().GetAsString(), e.reason().c_str()); return false; } return true; diff --git a/lib/mayaUsd/utils/traverseLayer.h b/lib/mayaUsd/utils/traverseLayer.h index c9e21606a9..a23b8159bc 100644 --- a/lib/mayaUsd/utils/traverseLayer.h +++ b/lib/mayaUsd/utils/traverseLayer.h @@ -32,10 +32,14 @@ namespace MAYAUSD_NS_DEF { class TraversalFailure : public std::runtime_error { public: - TraversalFailure(const std::string& reason, const PXR_NS::SdfPath& path) : - std::runtime_error(path.GetAsString()), _reason(reason), _path(path) {} + TraversalFailure(const std::string& reason, const PXR_NS::SdfPath& path) + : std::runtime_error(path.GetAsString()) + , _reason(reason) + , _path(path) + { + } TraversalFailure(const TraversalFailure&) = default; - ~TraversalFailure() override {} + ~TraversalFailure() override { } std::string reason() const { return _reason; } PXR_NS::SdfPath path() const { return _path; } @@ -45,7 +49,7 @@ class TraversalFailure : public std::runtime_error const PXR_NS::SdfPath _path; }; -//! \brief Type definition for layer traversal function. +//! \brief Type definition for layer traversal function. // // A layer traversal function must return true to continue the traversal, and // false to prune traversal to the children of the argument path. The @@ -66,8 +70,7 @@ MAYAUSD_CORE_PUBLIC bool traverseLayer( const PXR_NS::SdfLayerHandle& layer, const PXR_NS::SdfPath& path, - const TraverseLayerFn& fn -); + const TraverseLayerFn& fn); } // namespace MAYAUSD_NS_DEF diff --git a/lib/mayaUsd/utils/util.cpp b/lib/mayaUsd/utils/util.cpp index 4e823770b1..f5e04c48ca 100644 --- a/lib/mayaUsd/utils/util.cpp +++ b/lib/mayaUsd/utils/util.cpp @@ -2115,7 +2115,7 @@ MDagPath UsdMayaUtil::nameToDagPath(const std::string& name) MDagPath dag; // Not found? Empty selection list. if (!selection.isEmpty()) { - MStatus status = selection.getDagPath(0, dag); + MStatus status = selection.getDagPath(0, dag); CHECK_MSTATUS(status); } return dag; From 63a5725930fdec9e401413001c8acc3fc107ea2f Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Thu, 28 Oct 2021 13:17:27 -0400 Subject: [PATCH 03/12] Fixed compilation errors. --- lib/mayaUsd/fileio/primUpdaterManager.cpp | 2 +- lib/mayaUsd/utils/traverseLayer.cpp | 30 ++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/mayaUsd/fileio/primUpdaterManager.cpp b/lib/mayaUsd/fileio/primUpdaterManager.cpp index 13491bf55a..ffbecf3b15 100644 --- a/lib/mayaUsd/fileio/primUpdaterManager.cpp +++ b/lib/mayaUsd/fileio/primUpdaterManager.cpp @@ -611,7 +611,7 @@ bool PushCustomize( } catch (const MayaUsd::TraversalFailure& e) { TF_WARN( "PushEnd() layer traversal failed for path %s: %s", - e.path().GetAsString(), + e.path().GetText(), e.reason().c_str()); return false; } diff --git a/lib/mayaUsd/utils/traverseLayer.cpp b/lib/mayaUsd/utils/traverseLayer.cpp index c3ae67249f..30d38e43c5 100644 --- a/lib/mayaUsd/utils/traverseLayer.cpp +++ b/lib/mayaUsd/utils/traverseLayer.cpp @@ -13,6 +13,34 @@ // See the License for the specific language governing permissions and // limitations under the License. // +// Adapted from Pixar's USD SdfLayer::Traverse() implementation, so portions +// are copyrighted by Pixar, copyright notice follows: +// +// Copyright 2016 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// +/// +/// \file Sdf/layer.cpp + #include "traverseLayer.h" #include @@ -97,7 +125,7 @@ bool traverseLayer( _traverseLayer(layer, path, fn); } catch (const TraversalFailure& e) { TF_WARN( - "Layer traversal failed for path %s: %s", e.path().GetAsString(), e.reason().c_str()); + "Layer traversal failed for path %s: %s", e.path().GetText(), e.reason().c_str()); return false; } return true; From 0e456289c117f566ae3a786e1d2b114edaaca997 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Thu, 28 Oct 2021 13:45:55 -0400 Subject: [PATCH 04/12] Fix clang-format. --- lib/mayaUsd/utils/traverseLayer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/mayaUsd/utils/traverseLayer.cpp b/lib/mayaUsd/utils/traverseLayer.cpp index 30d38e43c5..1345cbe206 100644 --- a/lib/mayaUsd/utils/traverseLayer.cpp +++ b/lib/mayaUsd/utils/traverseLayer.cpp @@ -124,8 +124,7 @@ bool traverseLayer( try { _traverseLayer(layer, path, fn); } catch (const TraversalFailure& e) { - TF_WARN( - "Layer traversal failed for path %s: %s", e.path().GetText(), e.reason().c_str()); + TF_WARN("Layer traversal failed for path %s: %s", e.path().GetText(), e.reason().c_str()); return false; } return true; From 5e786250fc2a080e5e2a9bee19c3e408855da479 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Thu, 28 Oct 2021 14:16:27 -0400 Subject: [PATCH 05/12] Fix for older USD versions. --- lib/mayaUsd/utils/traverseLayer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mayaUsd/utils/traverseLayer.h b/lib/mayaUsd/utils/traverseLayer.h index a23b8159bc..74e74004ad 100644 --- a/lib/mayaUsd/utils/traverseLayer.h +++ b/lib/mayaUsd/utils/traverseLayer.h @@ -33,7 +33,7 @@ class TraversalFailure : public std::runtime_error { public: TraversalFailure(const std::string& reason, const PXR_NS::SdfPath& path) - : std::runtime_error(path.GetAsString()) + : std::runtime_error(path.GetString()) , _reason(reason) , _path(path) { From f11553551b8b3b8be4351d45f0845755cc3b39c3 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Thu, 28 Oct 2021 21:02:45 -0400 Subject: [PATCH 06/12] Revert channel box observer workaround to use dev branch fix. --- lib/mayaUsd/ufe/UsdUIUfeObserver.cpp | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/mayaUsd/ufe/UsdUIUfeObserver.cpp b/lib/mayaUsd/ufe/UsdUIUfeObserver.cpp index 66298bf447..f7b442577e 100644 --- a/lib/mayaUsd/ufe/UsdUIUfeObserver.cpp +++ b/lib/mayaUsd/ufe/UsdUIUfeObserver.cpp @@ -77,23 +77,17 @@ void UsdUIUfeObserver::operator()(const Ufe::Notification& notification) "mainChannelBox;"); MStringArray paths; if (MGlobal::executeCommand(mainObjListCmd, paths) && (paths.length() > 0)) { - // Under certain circumstances a USD attribute change causes - // the xformOpOrder attribute to change while the channel box - // is displaying a Maya object. This Maya object is returned - // without Maya path component separators (e.g. "Xform2"), - // which triggers UFE single-segment path construction, but - // there is none in Maya for any run-time, so an exception is - // thrown and we crash. Unconditionally refresh the channel - // box for now. PPT, 20-Oct-2021. -#ifdef SINGLE_SEGMENT_PATH_CRASH - auto ufePath = Ufe::PathString::path(paths[0].asChar()); + // Skip any non-absolute Maya paths (we know non-Maya ufe paths will always + // start with | + MString firstPath = paths[0]; + if (firstPath.substringW(0, 0) != "|") + return; + + auto ufePath = Ufe::PathString::path(firstPath.asChar()); if (ufePath.startsWith(ac->path())) { -#endif static const MString updateCBCmd("channelBox -e -update mainChannelBox;"); MGlobal::executeCommand(updateCBCmd); -#ifdef SINGLE_SEGMENT_PATH_CRASH } -#endif } } } From fa9281527ebbe5698343f31260902b19d56dde3d Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Mon, 1 Nov 2021 16:30:58 -0400 Subject: [PATCH 07/12] Addressed code review comments. --- lib/mayaUsd/fileio/CMakeLists.txt | 3 - lib/mayaUsd/fileio/inMemoryTranslation.py | 325 ------------------ lib/mayaUsd/fileio/primUpdater.cpp | 102 +----- lib/mayaUsd/fileio/primUpdater.h | 25 +- lib/mayaUsd/fileio/primUpdaterManager.cpp | 111 ++++-- lib/mayaUsd/fileio/primUpdaterManager.h | 27 +- lib/mayaUsd/python/wrapPrimUpdaterManager.cpp | 18 +- lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp | 4 +- lib/mayaUsd/ufe/PulledObjectHierarchy.cpp | 24 +- lib/mayaUsd/ufe/PulledObjectHierarchy.h | 32 +- .../ufe/PulledObjectHierarchyHandler.cpp | 6 +- .../ufe/PulledObjectHierarchyHandler.h | 15 +- lib/mayaUsd/ufe/UsdHierarchy.cpp | 4 +- lib/mayaUsd/ufe/UsdPathMappingHandler.cpp | 4 +- lib/usd/translators/mayaReferenceUpdater.cpp | 8 +- lib/usd/translators/mayaReferenceUpdater.h | 4 +- plugin/adsk/plugin/plugin.cpp | 2 +- 17 files changed, 173 insertions(+), 541 deletions(-) delete mode 100644 lib/mayaUsd/fileio/inMemoryTranslation.py diff --git a/lib/mayaUsd/fileio/CMakeLists.txt b/lib/mayaUsd/fileio/CMakeLists.txt index 2348380e89..d9460650b3 100644 --- a/lib/mayaUsd/fileio/CMakeLists.txt +++ b/lib/mayaUsd/fileio/CMakeLists.txt @@ -84,9 +84,6 @@ install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${PROJECT_NAME}/fileio ) -set(PYTHON_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/lib/python/${PROJECT_NAME}/lib) -install(FILES inMemoryTranslation.py DESTINATION ${PYTHON_INSTALL_PREFIX}) - # ----------------------------------------------------------------------------- # subdirectories # ----------------------------------------------------------------------------- diff --git a/lib/mayaUsd/fileio/inMemoryTranslation.py b/lib/mayaUsd/fileio/inMemoryTranslation.py deleted file mode 100644 index 066c58c4e4..0000000000 --- a/lib/mayaUsd/fileio/inMemoryTranslation.py +++ /dev/null @@ -1,325 +0,0 @@ -import maya.cmds as cmds -import maya.api.OpenMaya as om -import maya.api.OpenMayaAnim as oma -import mayaUsd.lib as mayaUsdLib -from mayaUsd.lib import proxyAccessor as pa -import ufe -import re - -from pxr import Usd, Sdf - -# --------------------------------- UTILITIES ---------------------------------------------- -def isAnimated(dagPath): - if cmds.evaluationManager( query=True, invalidate=True ): - upstream = cmds.evaluationManager(ust=dagPath) - countNonProxyShape = len(upstream) - for node in upstream: - if cmds.nodeType(node) == 'mayaUsdProxyShape': - countNonProxyShape -= 1 - print(countNonProxyShape) - return countNonProxyShape > 0 - else: - sel_list = om.MSelectionList() - sel_list.add(dagPath) - dep_node = sel_list.getDependNode(0) - return oma.MAnimUtil.isAnimated(dep_node) - -def pullSetName(): - return 'pullStateSet' - -def pullPrimMetadataKey(): - return 'Maya:Pull:DagPath' - -def pullDGMetadataKey(): - return 'Pull_UfePath' - -def dagPathToSdfPath(longDagPath): - path = re.sub(r'[|]',r'/',longDagPath) - sdfPath = Sdf.Path(re.sub(r'[^a-zA-Z0-9_/]',r'_',path)) - return sdfPath - -def sdfPathToDagPath(sdfPath): - dagPath = re.sub(r'/',r'|',sdfPath) - return dagPath - -def persistPullInformation(srcUfePath,dstDagPath): - setName = pullSetName() - - if setName not in cmds.listSets(allSets=True): - cmds.sets(em=True,n=setName) - - cmds.sets([dstDagPath],add=setName) - - # Store on src prim as custom metadata - shapePath, srcSdfPath = pa.getDagAndPrimFromUfe(srcUfePath) - stage = mayaUsdLib.GetPrim(shapePath).GetStage() - srcPrim = stage.GetPrimAtPath(srcSdfPath) - srcPrim.SetCustomDataByKey(pullPrimMetadataKey(), dstDagPath) - # Store on dst dg node as metadata - if not cmds.attributeQuery(pullDGMetadataKey(), node=dstDagPath, exists=True): - cmds.addAttr(dstDagPath,longName=pullDGMetadataKey(), dataType='string') - cmds.setAttr('{}.{}'.format(dstDagPath, pullDGMetadataKey()), ufe.PathString.string(srcUfePath.path()), type='string') - -def addExcludeFromRendering(shapePath, sdfPath): - excludedPrimsString = cmds.getAttr('{}.excludePrimPaths'.format(shapePath)) or '' - excludedPrims = excludedPrimsString.split(',') - excludedPrims.insert(0,sdfPath) - excludedPrimsString = ','.join(set(excludedPrims)) - cmds.setAttr('{}.excludePrimPaths'.format(shapePath), excludedPrimsString, type='string') - -def removeExcludeFromRendering(shapePath, sdfPath): - excludedPrimsString = cmds.getAttr('{}.excludePrimPaths'.format(shapePath)) or '' - excludedPrims = excludedPrimsString.split(',') - if sdfPath in excludedPrims: - excludedPrims.remove(sdfPath) - excludedPrimsString = ','.join(excludedPrims) - cmds.setAttr('{}.excludePrimPaths'.format(shapePath), excludedPrimsString, type='string') - -def getPullInformationFromDag(dagPath): - setName = pullSetName() - - if setName not in cmds.listSets(allSets=True): - return None - - if not cmds.sets([dagPath],im=setName): - return None - - if not cmds.attributeQuery(pullDGMetadataKey(), node=dagPath, exists=True): - print('Error - DAG path is in pulled set but does not have pulled path stored') - return None - - pulledUfePathString = cmds.getAttr('{}.{}'.format(dagPath,pullDGMetadataKey())) or '' - - pulledUfePath = ufe.PathString.path(pulledUfePathString) - pulledUfeItem = ufe.Hierarchy.createItem(pulledUfePath) - - return pulledUfeItem - -def getPullInformationFromUsd(ufePath): - shapePath, sdfPath = pa.getDagAndPrimFromUfe(ufePath) - if not (shapePath and sdfPath): - return None - - stage = mayaUsdLib.GetPrim(shapePath).GetStage() - srcPrim = stage.GetPrimAtPath(sdfPath) - pulledDagPath = srcPrim.GetCustomDataByKey(pullPrimMetadataKey()) - - return pulledDagPath - -def getPullInformationFromPrim(usdPrim): - pulledDagPath = usdPrim.GetCustomDataByKey(pullPrimMetadataKey()) - - return pulledDagPath - -# --------------------------------- PUSH & PULL ---------------------------------------------- -def pull(ufePath, srcLayer, asCopy=False): - shapePath, sdfPath = pa.getDagAndPrimFromUfe(ufePath) - - if shapePath == None or sdfPath == None: - return - - if srcLayer == None: - srcLayer = mayaUsdLib.GetPrim(shapePath).GetStage().GetRootLayer() - - if sdfPath == None: - sdfPath = Sdf.Path.absoluteRootPath - - cmds.mayaUSDImport(f=srcLayer.identifier, primPath=sdfPath, readAnimData=True) - - # put objects in the right space - parentPath = Sdf.Path(sdfPath).GetParentPath() - childDagPath = sdfPathToDagPath('|'+Sdf.Path(sdfPath).name) - ufeChildren = [pa.createUfeSceneItem(childDagPath)] - if parentPath != Sdf.Path.absoluteRootPath: - ufeParent = pa.createUfeSceneItem(shapePath,parentPath.pathString) - else: - ufeParent = pa.createUfeSceneItem(shapePath) - pa.parentItems(ufeChildren, ufeParent) - - if not asCopy: - #store the linkage between prim and dag in metadata - persistPullInformation(ufePath,childDagPath) - - #prevent doouble rendering by excluding pulled prim from rendering - addExcludeFromRendering(shapePath, sdfPath) - - return childDagPath - -def pushClear(ufePush): - pulledShapePath = None - pulledPrim = None - pushDagPath = None - - if pa.isUfeUsdPath(ufePush): - pulledShapePath, pulledSdfPathString = pa.getDagAndPrimFromUfe(ufePush) - pulledStage = mayaUsdLib.GetPrim(pulledShapePath).GetStage() - pulledPrim = pulledStage.GetPrimAtPath(pulledSdfPathString) - pushDagPath = pulledPrim.GetCustomDataByKey(pullPrimMetadataKey()) - else: - pushDagPath, pushSdfPath = pa.getDagAndPrimFromUfe(ufePush) - pulledUfePath = getPullInformationFromDag(pushDagPath) - pulledShapePath, pulledSdfPathString = pa.getDagAndPrimFromUfe(pulledUfePath) - - pulledStage = mayaUsdLib.GetPrim(pulledShapePath).GetStage() - pulledPrim = pulledStage.GetPrimAtPath(pulledSdfPathString) - - # Cleanup pull infromation and re-enable rendering from stage - removeExcludeFromRendering(pulledShapePath,pulledPrim.GetPath().pathString) - cmds.delete(pushDagPath) - pulledPrim.ClearCustomDataByKey(pullPrimMetadataKey()) - -def push(dagPath, dstLayer, dstSdfPath=None, withAnimation=True): - if dstSdfPath == Sdf.Path.absoluteRootPath: - print('Pushing state over pseudo root is not allowed') - return False - - srcLayer = Sdf.Layer.CreateAnonymous() - cmds.select(dagPath); - if withAnimation and isAnimated(dagPath): - cmds.mayaUSDExport(f=srcLayer.identifier, sl=True, frameSample=1.0, frameRange=[cmds.playbackOptions(q=True, min=True),cmds.playbackOptions(q=True, max=True)]) - else: - cmds.mayaUSDExport(f=srcLayer.identifier, sl=True) - - stage = Usd.Stage.Open(srcLayer) - srcPrim = stage.GetPrimAtPath(dagPathToSdfPath(dagPath)) - if dstSdfPath: - Sdf.CopySpec(srcLayer, srcPrim.GetPath(), dstLayer, dstSdfPath) - else: - Sdf.CopySpec(srcLayer, srcPrim.GetPath(), dstLayer, srcPrim.GetPath()) - - # Copy everything (not used anymore...left here simply because this script is not yet under source control) - #root = stage.GetPrimAtPath(Sdf.Path.absoluteRootPath) - #for child in root.GetAllChildren(): - # Sdf.CopySpec(srcLayer, child.GetPath(), dstLayer, child.GetPath()) - - return True - -def pushBack(ufePush): - pushDagPath, pushSdfPath = pa.getDagAndPrimFromUfe(ufePush) - if pushSdfPath: - print('Not implemented pull from usd selection - but we have everything stored to make it') - else: - pulledUfePath = getPullInformationFromDag(pushDagPath) - pulledShapePath, pulledSdfPathString = pa.getDagAndPrimFromUfe(pulledUfePath) - - pulledStage = mayaUsdLib.GetPrim(pulledShapePath).GetStage() - - dstLayer = pulledStage.GetRootLayer() - - if push(pushDagPath,dstLayer,Sdf.Path(pulledSdfPathString)): - pulledPrim = pulledStage.GetPrimAtPath(pulledSdfPathString) - # Cleanup pull infromation and re-enable rendering from stage - removeExcludeFromRendering(pulledShapePath,pulledSdfPathString) - cmds.delete(pushDagPath) - # Since we pushed over existing prim, the custom metadata should be already cleared - pulledPrim.ClearCustomDataByKey(pullPrimMetadataKey()) - -def pushBetween(ufeSrc,ufeDst): - if pa.isUfeUsdPath(ufeSrc): - print('Must select first Dag object') - return - - srcDagPath, srcSdfPath = pa.getDagAndPrimFromUfe(ufeSrc) - shapePath, dstSdfPath = pa.getDagAndPrimFromUfe(ufeDst) - - dstLayer = mayaUsdLib.GetPrim(shapePath).GetStage().GetRootLayer() - - if dstSdfPath and dstSdfPath != Sdf.Path.absoluteRootPath: - push(srcDagPath, dstLayer, Sdf.Path(dstSdfPath)) - else: - push(srcDagPath, dstLayer) - -def pushToVariant(srcDagPath, dstSdfPath, dstLayer, dstVariantSetName, dstVariantName): - srcLayer = Sdf.Layer.CreateAnonymous() - cmds.mayaUSDExport(f=srcLayer.identifier, sl=True) - - dstStage = Usd.Stage.Open(dstLayer) - dstPrim = dstStage.GetPrimAtPath(dstSdfPath) - dstVariantSet = dstPrim.GetVariantSets().AddVariantSet(dstVariantSetName) - dstVariantSet.AddVariant(dstVariantName) - dstVariantSet.SetVariantSelection(dstVariantName) - with dstVariantSet.GetVariantEditContext(): - stage = Usd.Stage.Open(srcLayer) - root = stage.GetPrimAtPath(Sdf.Path.absoluteRootPath) - for child in root.GetAllChildren(): - #targetPrimPath = dstPrim.GetPath().AppendPath(dstVariantName) - #dstStage.DefinePrim(targetPrimPath, 'Scope') - dstPrimPathWithVariant = dstPrim.GetPath().AppendVariantSelection(dstVariantSetName, dstVariantName) - dstPrimPathWithVariant = child.GetPath().ReplacePrefix(Sdf.Path.absoluteRootPath,dstPrimPathWithVariant) - print(dstPrimPathWithVariant) - Sdf.CopySpec(srcLayer, child.GetPath(), dstLayer, dstPrimPathWithVariant) - -# -------------------------------------- WORKFLOWS ------------------------------------------ - -def pullSelection(): - pulledDagPath = pull(pa.getUfeSelection(),None) or '' - cmds.select(pulledDagPath) - -def pushSelection(): - ufeSelection = ufe.GlobalSelection.get() - if len(ufeSelection) == 2: - ufeSelectionIter = iter(ufeSelection) - - ufeSrc = next(ufeSelectionIter) - ufeDst = next(ufeSelectionIter) - - pushBetween(ufeSrc,ufeDst) - elif len(ufeSelection) == 1: - ufePush = next(iter(ufeSelection)) - pushBack(ufePush) - else: - print('Unhandled push from selection') - -def pushClearSelection(): - ufeSelection = ufe.GlobalSelection.get() - if len(ufeSelection) == 1: - ufePush = next(iter(ufeSelection)) - pushClear(ufePush) - else: - print('Unhandled push clear from selection') - -def pushSelectionToVariant(variantSetName, variantName): - ufeSelection = ufe.GlobalSelection.get() - if len(ufeSelection) != 2: - print('Must select exactly two objects') - return - - ufeSelectionIter = iter(ufeSelection) - - ufeSrc = next(ufeSelectionIter) - ufeDst = next(ufeSelectionIter) - - ufeSelectionIter = None - - if pa.isUfeUsdPath(ufeSrc): - print('Must select first Dag object') - return - - if not pa.isUfeUsdPath(ufeDst): - print('Must select second ufe-usd object') - return - - srcDagPath, srcSdfPath = pa.getDagAndPrimFromUfe(ufeSrc) - shapePath, dstSdfPath = pa.getDagAndPrimFromUfe(ufeDst) - - dstLayer = mayaUsdLib.GetPrim(shapePath).GetStage().GetRootLayer() - - pushToVariant(srcDagPath, dstSdfPath, dstLayer, variantSetName, variantName) - -def assignMaterialToSelection(material): - ufeItem = pa.getUfeSelection() - shapePath, sdfPath = pa.getDagAndPrimFromUfe(ufeItem) - - tmpDagPath = sdfPathToDagPath(sdfPath) - - pull(ufeItem,None) - - cmds.select(tmpDagPath); - cmds.sets(e=True,forceElement=material) - cmds.select(cl=True) - - dstLayer = mayaUsdLib.GetPrim(shapePath).GetStage().GetRootLayer() - push(tmpDagPath, dstLayer) - - cmds.delete(tmpDagPath) - diff --git a/lib/mayaUsd/fileio/primUpdater.cpp b/lib/mayaUsd/fileio/primUpdater.cpp index df2f7b7222..34ee560a5a 100644 --- a/lib/mayaUsd/fileio/primUpdater.cpp +++ b/lib/mayaUsd/fileio/primUpdater.cpp @@ -58,53 +58,29 @@ extern Ufe::Rtid g_MayaRtid; PXR_NAMESPACE_OPEN_SCOPE -namespace { -// Set name that will be used to hold all pulled objects -// MOVED -const std::string kPullSetName("pullStateSet"); - -// Metadata key used to store pull information on a prim -// MOVED -const TfToken kPullPrimMetadataKey("Maya:Pull:DagPath"); - -// Metadata key used to store pull information on a DG node -// MOVED -const MString kPullDGMetadataKey("Pull_UfePath"); - -Ufe::Path usdToMaya(const std::string& pathStr) -{ - Ufe::PathSegment seg( - Ufe::PathSegment::Components({ Ufe::PathComponent("world"), Ufe::PathComponent(pathStr) }), - MayaUsd::ufe::g_MayaRtid, - '|'); - return Ufe::Path(seg); -} - -} // namespace - UsdMayaPrimUpdater::UsdMayaPrimUpdater(const MFnDependencyNode& depNodeFn, const Ufe::Path& path) : _mayaObject(depNodeFn.object()) , _path(path) { } -bool UsdMayaPrimUpdater::Pull(const UsdMayaPrimUpdaterContext& context) { return true; } +bool UsdMayaPrimUpdater::pull(const UsdMayaPrimUpdaterContext& context) { return true; } -bool UsdMayaPrimUpdater::DiscardEdits(const UsdMayaPrimUpdaterContext& context) +bool UsdMayaPrimUpdater::discardEdits(const UsdMayaPrimUpdaterContext& context) { - MObject objectToDelete = GetMayaObject(); + MObject objectToDelete = getMayaObject(); if (!objectToDelete.isNull()) { MGlobal::deleteNode(objectToDelete); } return true; } -bool UsdMayaPrimUpdater::PushEnd(const UsdMayaPrimUpdaterContext& context) +bool UsdMayaPrimUpdater::pushEnd(const UsdMayaPrimUpdaterContext& context) { - return DiscardEdits(context); + return discardEdits(context); } -bool UsdMayaPrimUpdater::PushCopySpecs( +bool UsdMayaPrimUpdater::pushCopySpecs( SdfLayerRefPtr srcLayer, const SdfPath& topSrcPath, SdfLayerRefPtr dstLayer, @@ -176,75 +152,15 @@ bool UsdMayaPrimUpdater::PushCopySpecs( srcLayer, topSrcPath, dstLayer, topDstPath, shouldCopyValueFn, dontCopyChildrenFn); } -const MObject& UsdMayaPrimUpdater::GetMayaObject() const { return _mayaObject; } +const MObject& UsdMayaPrimUpdater::getMayaObject() const { return _mayaObject; } -const Ufe::Path& UsdMayaPrimUpdater::GetUfePath() const { return _path; } +const Ufe::Path& UsdMayaPrimUpdater::getUfePath() const { return _path; } -UsdPrim UsdMayaPrimUpdater::GetUsdPrim(const UsdMayaPrimUpdaterContext& context) const +UsdPrim UsdMayaPrimUpdater::getUsdPrim(const UsdMayaPrimUpdaterContext& context) const { return MayaUsd::ufe::ufePathToPrim(_path); } -/* static */ -bool UsdMayaPrimUpdater::readPullInformation(const PXR_NS::UsdPrim& prim, std::string& dagPathStr) -{ - auto value = prim.GetCustomDataByKey(kPullPrimMetadataKey); - if (!value.IsEmpty() && value.CanCast()) { - dagPathStr = value.Get(); - return !dagPathStr.empty(); - } - return false; -} - -/* static */ -bool UsdMayaPrimUpdater::readPullInformation( - const PXR_NS::UsdPrim& prim, - Ufe::SceneItem::Ptr& dagPathItem) -{ - std::string dagPathStr; - if (readPullInformation(prim, dagPathStr)) { - // Remove leading '|' character. - if (dagPathStr[0] == '|') - dagPathStr = dagPathStr.substr(1); - dagPathItem = Ufe::Hierarchy::createItem(usdToMaya(dagPathStr)); - return (bool)dagPathItem; - } - return false; -} - -/* static */ -bool UsdMayaPrimUpdater::readPullInformation(const Ufe::Path& ufePath, MDagPath& dagPath) -{ - auto prim = MayaUsd::ufe::ufePathToPrim(ufePath); - std::string dagPathStr; - if (readPullInformation(prim, dagPathStr)) { - MSelectionList sel; - sel.add(dagPathStr.c_str()); - sel.getDagPath(0, dagPath); - return dagPath.isValid(); - } - return false; -} - -/* static */ -bool UsdMayaPrimUpdater::readPullInformation(const MDagPath& dagPath, Ufe::Path& ufePath) -{ - MStatus status; - - MFnDependencyNode depNode(dagPath.node()); - MPlug dgMetadata = depNode.findPlug(kPullDGMetadataKey, &status); - if (status == MStatus::kSuccess) { - MString pulledUfePathStr; - status = dgMetadata.getValue(pulledUfePathStr); - if (status) { - ufePath = Ufe::PathString::path(pulledUfePathStr.asChar()); - return !ufePath.empty(); - } - } - - return false; -} - /* static */ bool UsdMayaPrimUpdater::isAnimated(const MDagPath& path) { diff --git a/lib/mayaUsd/fileio/primUpdater.h b/lib/mayaUsd/fileio/primUpdater.h index fe6a93eb23..d2d6b048f2 100644 --- a/lib/mayaUsd/fileio/primUpdater.h +++ b/lib/mayaUsd/fileio/primUpdater.h @@ -29,7 +29,6 @@ #include #include #include -#include PXR_NAMESPACE_OPEN_SCOPE @@ -59,7 +58,7 @@ class UsdMayaPrimUpdater // Copy the pushed prim from the temporary srcLayer where it has been // exported by push into the destination dstLayer which is in the scene. MAYAUSD_CORE_PUBLIC - virtual bool PushCopySpecs( + virtual bool pushCopySpecs( SdfLayerRefPtr srcLayer, const SdfPath& srcSdfPath, SdfLayerRefPtr dstLayer, @@ -68,38 +67,30 @@ class UsdMayaPrimUpdater /// Customize the pulled prim after pull import. Default implementation in /// this class is a no-op. MAYAUSD_CORE_PUBLIC - virtual bool Pull(const UsdMayaPrimUpdaterContext& context); + virtual bool pull(const UsdMayaPrimUpdaterContext& context); /// Discard edits done in Maya. Implementation in this class removes the /// Maya node. MAYAUSD_CORE_PUBLIC - virtual bool DiscardEdits(const UsdMayaPrimUpdaterContext& context); + virtual bool discardEdits(const UsdMayaPrimUpdaterContext& context); /// Clean up Maya data model at end of push. Implementation in this class - /// calls DiscardEdits(). + /// calls discardEdits(). MAYAUSD_CORE_PUBLIC - virtual bool PushEnd(const UsdMayaPrimUpdaterContext& context); + virtual bool pushEnd(const UsdMayaPrimUpdaterContext& context); /// The MObject for the Maya node being updated by this updater. MAYAUSD_CORE_PUBLIC - const MObject& GetMayaObject() const; + const MObject& getMayaObject() const; /// The path of the destination USD prim which we are updating. MAYAUSD_CORE_PUBLIC - const Ufe::Path& GetUfePath() const; + const Ufe::Path& getUfePath() const; /// The destination USD prim which we are updating. MAYAUSD_CORE_PUBLIC - UsdPrim GetUsdPrim(const UsdMayaPrimUpdaterContext& context) const; + UsdPrim getUsdPrim(const UsdMayaPrimUpdaterContext& context) const; - MAYAUSD_CORE_PUBLIC - static bool readPullInformation(const PXR_NS::UsdPrim& prim, std::string& dagPathStr); - MAYAUSD_CORE_PUBLIC - static bool readPullInformation(const PXR_NS::UsdPrim& prim, Ufe::SceneItem::Ptr& dagPathItem); - MAYAUSD_CORE_PUBLIC - static bool readPullInformation(const Ufe::Path& ufePath, MDagPath& dagPath); - MAYAUSD_CORE_PUBLIC - static bool readPullInformation(const MDagPath& dagpath, Ufe::Path& ufePath); MAYAUSD_CORE_PUBLIC static bool isAnimated(const MDagPath& path); diff --git a/lib/mayaUsd/fileio/primUpdaterManager.cpp b/lib/mayaUsd/fileio/primUpdaterManager.cpp index ffbecf3b15..6f8eaee1cc 100644 --- a/lib/mayaUsd/fileio/primUpdaterManager.cpp +++ b/lib/mayaUsd/fileio/primUpdaterManager.cpp @@ -81,6 +81,9 @@ const TfToken kPullPrimMetadataKey("Maya:Pull:DagPath"); // Metadata key used to store pull information on a DG node const MString kPullDGMetadataKey("Pull_UfePath"); +// Name of Dag node under which all pulled sub-hierarchies are rooted. +const MString kPullRootName("__mayaUsd__"); + //! Lock or unlock hierarchy starting at given root. void lockNodes(const MDagPath& root, bool state) { @@ -103,7 +106,7 @@ Ufe::Path usdToMaya(const Ufe::Path& usdPath) return Ufe::Path(); } std::string dagPathStr; - if (!TF_VERIFY(PXR_NS::UsdMayaPrimUpdater::readPullInformation(prim, dagPathStr))) { + if (!TF_VERIFY(PXR_NS::PrimUpdaterManager::readPullInformation(prim, dagPathStr))) { return Ufe::Path(); } @@ -369,7 +372,7 @@ bool PullCustomize(const PullImportPaths& importedPaths, const UsdMayaPrimUpdate // customization step. This is a frequent difficulty for operations on // multiple data, especially since we can't roll back the result of // the execution of previous updaters. Revisit this. PPT, 15-Sep-2021. - if (!updater->Pull(context)) { + if (!updater->pull(context)) { return false; } } @@ -558,8 +561,8 @@ bool PushCustomize( auto relativeSrcPath = srcPath.MakeRelativePath(SdfPath::AbsoluteRootPath()); auto dstPath = dstRootParentPath.AppendPath(relativeSrcPath); - // Report PushCopySpecs() failure. - if (!updater->PushCopySpecs(srcLayer, srcPath, dstLayer, dstPath)) { + // Report pushCopySpecs() failure. + if (!updater->pushCopySpecs(srcLayer, srcPath, dstLayer, dstPath)) { throw MayaUsd::TraversalFailure(std::string("PushCopySpecs() failed."), srcPath); } @@ -598,8 +601,8 @@ bool PushCustomize( return; } - // Report PushEnd() failure. - if (!updater->PushEnd(context)) { + // Report pushEnd() failure. + if (!updater->pushEnd(context)) { throw MayaUsd::TraversalFailure(std::string("PushEnd() failed."), primSpecPath); } }; @@ -640,8 +643,6 @@ class PushPullScope bool* _controlingFlag { nullptr }; }; -MString pullRootName("__mayaUsd__"); - } // namespace PXR_NAMESPACE_OPEN_SCOPE @@ -671,7 +672,7 @@ PrimUpdaterManager::~PrimUpdaterManager() _cbIds.clear(); } -bool PrimUpdaterManager::Push(const MFnDependencyNode& depNodeFn, const Ufe::Path& pulledPath) +bool PrimUpdaterManager::push(const MFnDependencyNode& depNodeFn, const Ufe::Path& pulledPath) { MayaUsdProxyShapeBase* proxyShape = MayaUsd::ufe::getProxyShape(pulledPath); if (!proxyShape) { @@ -729,7 +730,7 @@ bool PrimUpdaterManager::Push(const MFnDependencyNode& depNodeFn, const Ufe::Pat } if (!isCopy) { - if (!TF_VERIFY(RemovePullParent(pullParentPath))) { + if (!TF_VERIFY(removePullParent(pullParentPath))) { return false; } } @@ -743,7 +744,7 @@ bool PrimUpdaterManager::Push(const MFnDependencyNode& depNodeFn, const Ufe::Pat return true; } -bool PrimUpdaterManager::Pull(const Ufe::Path& path) +bool PrimUpdaterManager::pull(const Ufe::Path& path) { MayaUsdProxyShapeBase* proxyShape = MayaUsd::ufe::getProxyShape(path); if (!proxyShape) { @@ -762,7 +763,7 @@ bool PrimUpdaterManager::Pull(const Ufe::Path& path) MDagPath pullParentPath; if (!updaterArgs._copyOperation - && !(pullParentPath = SetupPullParent(path, importArgs)).isValid()) { + && !(pullParentPath = setupPullParent(path, importArgs)).isValid()) { return false; } @@ -799,7 +800,7 @@ bool PrimUpdaterManager::Pull(const Ufe::Path& path) return true; } -bool PrimUpdaterManager::PullClear(const Ufe::Path& pulledPath) +bool PrimUpdaterManager::discardEdits(const Ufe::Path& pulledPath) { MayaUsdProxyShapeBase* proxyShape = MayaUsd::ufe::getProxyShape(pulledPath); if (!proxyShape) { @@ -835,13 +836,13 @@ bool PrimUpdaterManager::PullClear(const Ufe::Path& pulledPath) dagIt.getPath(curDagPath); MFnDependencyNode depNodeFn(curDagPath.node()); FallbackPrimUpdater fallback(depNodeFn, Ufe::Path()); - fallback.DiscardEdits(context); + fallback.discardEdits(context); } removePullInformation(pulledPath); removeExcludeFromRendering(pulledPath); - if (!TF_VERIFY(RemovePullParent(pullParent))) { + if (!TF_VERIFY(removePullParent(pullParent))) { return false; } @@ -853,7 +854,7 @@ bool PrimUpdaterManager::PullClear(const Ufe::Path& pulledPath) return true; } -bool PrimUpdaterManager::CopyBetween(const Ufe::Path& srcPath, const Ufe::Path& dstPath) +bool PrimUpdaterManager::copyBetween(const Ufe::Path& srcPath, const Ufe::Path& dstPath) { MayaUsdProxyShapeBase* srcProxyShape = MayaUsd::ufe::getProxyShape(srcPath); MayaUsdProxyShapeBase* dstProxyShape = MayaUsd::ufe::getProxyShape(dstPath); @@ -968,18 +969,18 @@ void PrimUpdaterManager::onProxyContentChanged( = MayaUsd::ufe::usdPathToUfePathSegment(prim.GetPath()); const Ufe::Path path = proxyNotice.GetProxyShape().ufePath() + pathSegment; - Pull(path); + pull(path); } } } } -PrimUpdaterManager& PrimUpdaterManager::GetInstance() +PrimUpdaterManager& PrimUpdaterManager::getInstance() { return TfSingleton::GetInstance(); } -bool PrimUpdaterManager::FindOrCreatePullRoot() +bool PrimUpdaterManager::findOrCreatePullRoot() { // If we already found the pull root, good to go. if (!_pullRoot.isNull()) { @@ -993,7 +994,7 @@ bool PrimUpdaterManager::FindOrCreatePullRoot() for (unsigned int i = 0; i < nbWorldChildren; ++i) { auto childObj = world.child(i); MFnDependencyNode child(childObj); - if (child.name() == pullRootName) { + if (child.name() == kPullRootName) { _pullRoot = childObj; return true; } @@ -1006,7 +1007,7 @@ bool PrimUpdaterManager::FindOrCreatePullRoot() if (status != MStatus::kSuccess) { return false; } - status = dagMod.renameNode(pullRootObj, pullRootName); + status = dagMod.renameNode(pullRootObj, kPullRootName); if (status != MStatus::kSuccess) { return false; } @@ -1024,7 +1025,7 @@ bool PrimUpdaterManager::FindOrCreatePullRoot() return true; } -MObject PrimUpdaterManager::CreatePullParent(const Ufe::Path& pulledPath) +MObject PrimUpdaterManager::createPullParent(const Ufe::Path& pulledPath) { MDagModifier dagMod; MStatus status; @@ -1040,7 +1041,7 @@ MObject PrimUpdaterManager::CreatePullParent(const Ufe::Path& pulledPath) return (dagMod.doIt() == MStatus::kSuccess) ? pullParentObj : MObject::kNullObj; } -bool PrimUpdaterManager::RemovePullParent(const MDagPath& parentDagPath) +bool PrimUpdaterManager::removePullParent(const MDagPath& parentDagPath) { if (!TF_VERIFY(parentDagPath.isValid())) { return false; @@ -1065,13 +1066,13 @@ bool PrimUpdaterManager::RemovePullParent(const MDagPath& parentDagPath) return dgMod.doIt() == MStatus::kSuccess; } -MDagPath PrimUpdaterManager::SetupPullParent(const Ufe::Path& pulledPath, VtDictionary& args) +MDagPath PrimUpdaterManager::setupPullParent(const Ufe::Path& pulledPath, VtDictionary& args) { - if (!FindOrCreatePullRoot()) { + if (!findOrCreatePullRoot()) { return MDagPath(); } - auto pullParent = CreatePullParent(pulledPath); + auto pullParent = createPullParent(pulledPath); if (pullParent == MObject::kNullObj) { return MDagPath(); } @@ -1094,4 +1095,62 @@ void PrimUpdaterManager::beforeNewOrOpenCallback(void* clientData) auto um = static_cast(clientData); um->_pullRoot = MObject::kNullObj; } + +/* static */ +bool PrimUpdaterManager::readPullInformation(const PXR_NS::UsdPrim& prim, std::string& dagPathStr) +{ + auto value = prim.GetCustomDataByKey(kPullPrimMetadataKey); + if (!value.IsEmpty() && value.CanCast()) { + dagPathStr = value.Get(); + return !dagPathStr.empty(); + } + return false; +} + +/* static */ +bool PrimUpdaterManager::readPullInformation( + const PXR_NS::UsdPrim& prim, + Ufe::SceneItem::Ptr& dagPathItem) +{ + std::string dagPathStr; + if (readPullInformation(prim, dagPathStr)) { + dagPathItem = Ufe::Hierarchy::createItem(Ufe::PathString::path(dagPathStr)); + return (bool)dagPathItem; + } + return false; +} + +/* static */ +bool PrimUpdaterManager::readPullInformation(const Ufe::Path& ufePath, MDagPath& dagPath) +{ + auto prim = MayaUsd::ufe::ufePathToPrim(ufePath); + std::string dagPathStr; + if (readPullInformation(prim, dagPathStr)) { + MSelectionList sel; + sel.add(dagPathStr.c_str()); + sel.getDagPath(0, dagPath); + return dagPath.isValid(); + } + return false; +} + +/* static */ +bool PrimUpdaterManager::readPullInformation(const MDagPath& dagPath, Ufe::Path& ufePath) +{ + MStatus status; + + MFnDependencyNode depNode(dagPath.node()); + MPlug dgMetadata = depNode.findPlug(kPullDGMetadataKey, &status); + if (status == MStatus::kSuccess) { + MString pulledUfePathStr; + status = dgMetadata.getValue(pulledUfePathStr); + if (status) { + ufePath = Ufe::PathString::path(pulledUfePathStr.asChar()); + return !ufePath.empty(); + } + } + + return false; +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/fileio/primUpdaterManager.h b/lib/mayaUsd/fileio/primUpdaterManager.h index ea0c1a664d..26447ce2d5 100644 --- a/lib/mayaUsd/fileio/primUpdaterManager.h +++ b/lib/mayaUsd/fileio/primUpdaterManager.h @@ -30,6 +30,7 @@ #include #include #include +#include PXR_NAMESPACE_OPEN_SCOPE @@ -40,21 +41,29 @@ class PrimUpdaterManager : public PXR_NS::TfWeakBase ~PrimUpdaterManager(); MAYAUSD_CORE_PUBLIC - bool Push(const MFnDependencyNode& depNodeFn, const Ufe::Path& pulledPath); + bool push(const MFnDependencyNode& depNodeFn, const Ufe::Path& pulledPath); MAYAUSD_CORE_PUBLIC - bool Pull(const Ufe::Path& path); + bool pull(const Ufe::Path& path); MAYAUSD_CORE_PUBLIC - bool PullClear(const Ufe::Path& path); + bool discardEdits(const Ufe::Path& path); MAYAUSD_CORE_PUBLIC - bool CopyBetween(const Ufe::Path& srcPath, const Ufe::Path& dstPath); + bool copyBetween(const Ufe::Path& srcPath, const Ufe::Path& dstPath); /// \brief Returns the singleton prim updater manager MAYAUSD_CORE_PUBLIC - static PrimUpdaterManager& GetInstance(); + static PrimUpdaterManager& getInstance(); + MAYAUSD_CORE_PUBLIC + static bool readPullInformation(const PXR_NS::UsdPrim& prim, std::string& dagPathStr); + MAYAUSD_CORE_PUBLIC + static bool readPullInformation(const PXR_NS::UsdPrim& prim, Ufe::SceneItem::Ptr& dagPathItem); + MAYAUSD_CORE_PUBLIC + static bool readPullInformation(const Ufe::Path& ufePath, MDagPath& dagPath); + MAYAUSD_CORE_PUBLIC + static bool readPullInformation(const MDagPath& dagpath, Ufe::Path& ufePath); private: PrimUpdaterManager(); @@ -62,17 +71,17 @@ class PrimUpdaterManager : public PXR_NS::TfWeakBase //! Ensure the Dag pull root exists. This is the child of the Maya world //! node under which all pulled nodes are created. - bool FindOrCreatePullRoot(); + bool findOrCreatePullRoot(); //! Create the pull parent for the pulled hierarchy. This is the node //! which receives the pulled node's parent transformation. - MObject CreatePullParent(const Ufe::Path& pulledPath); + MObject createPullParent(const Ufe::Path& pulledPath); //! Remove the pull parent for the pulled hierarchy. - bool RemovePullParent(const MDagPath& pullParent); + bool removePullParent(const MDagPath& pullParent); //! Create the pull parent and set it into the prim updater context. - MDagPath SetupPullParent(const Ufe::Path& pulledPath, VtDictionary& args); + MDagPath setupPullParent(const Ufe::Path& pulledPath, VtDictionary& args); //! Maya scene message callback. static void beforeNewOrOpenCallback(void* clientData); diff --git a/lib/mayaUsd/python/wrapPrimUpdaterManager.cpp b/lib/mayaUsd/python/wrapPrimUpdaterManager.cpp index b387d144cb..613345e783 100644 --- a/lib/mayaUsd/python/wrapPrimUpdaterManager.cpp +++ b/lib/mayaUsd/python/wrapPrimUpdaterManager.cpp @@ -51,28 +51,28 @@ bool push(const std::string& nodeName) return false; Ufe::Path path; - if (!UsdMayaPrimUpdater::readPullInformation(dagPath, path)) + if (!PrimUpdaterManager::readPullInformation(dagPath, path)) return false; - return PrimUpdaterManager::GetInstance().Push(dagNode, path); + return PrimUpdaterManager::getInstance().push(dagNode, path); } bool pull(const std::string& ufePathString) { Ufe::Path path = Ufe::PathString::path(ufePathString); - return PrimUpdaterManager::GetInstance().Pull(path); + return PrimUpdaterManager::getInstance().pull(path); } -bool pullClear(const std::string& nodeName) +bool discardEdits(const std::string& nodeName) { auto dagPath = UsdMayaUtil::nameToDagPath(nodeName); Ufe::Path path; - if (!UsdMayaPrimUpdater::readPullInformation(dagPath, path)) + if (!PrimUpdaterManager::readPullInformation(dagPath, path)) return false; - return PrimUpdaterManager::GetInstance().PullClear(path); + return PrimUpdaterManager::getInstance().discardEdits(path); } bool copyBetween(const std::string& srcUfePathString, const std::string& dstUfePathString) @@ -83,7 +83,7 @@ bool copyBetween(const std::string& srcUfePathString, const std::string& dstUfeP if (src.empty() || dst.empty()) return false; - return PrimUpdaterManager::GetInstance().CopyBetween(src, dst); + return PrimUpdaterManager::getInstance().copyBetween(src, dst); } } // namespace @@ -94,9 +94,9 @@ void wrapPrimUpdaterManager() class_("PrimUpdaterManager", no_init) .def("push", push) .def("pull", pull) - .def("pullClear", pullClear) + .def("pullClear", discardEdits) .def("mergeToUsd", push) .def("editAsMaya", pull) - .def("discardEdits", pullClear) + .def("discardEdits", discardEdits) .def("copy", copyBetween); } diff --git a/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp b/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp index c5558955ed..2fd5ceb73e 100644 --- a/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp +++ b/lib/mayaUsd/ufe/ProxyShapeHierarchy.cpp @@ -35,7 +35,7 @@ #endif #ifdef UFE_V3_FEATURES_AVAILABLE -#include +#include #include // In UFE v2 but only needed for primUpdater. #endif @@ -179,7 +179,7 @@ Ufe::SceneItemList ProxyShapeHierarchy::createUFEChildList(const UsdPrimSiblingR UFE_V3(std::string dagPathStr;) for (const auto& child : range) { #ifdef UFE_V3_FEATURES_AVAILABLE - if (PXR_NS::UsdMayaPrimUpdater::readPullInformation(child, dagPathStr)) { + if (PXR_NS::PrimUpdaterManager::readPullInformation(child, dagPathStr)) { auto item = Ufe::Hierarchy::createItem(Ufe::PathString::path(dagPathStr)); if (TF_VERIFY(item, "No item for pulled path '%s'\n", dagPathStr.c_str())) { children.emplace_back(item); diff --git a/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp b/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp index 7a44de0302..9fd189e39c 100644 --- a/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp +++ b/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp @@ -1,5 +1,5 @@ // -// Copyright 2019 Autodesk +// 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. @@ -32,8 +32,7 @@ PulledObjectHierarchy::PulledObjectHierarchy( const Ufe::SceneItem::Ptr& item, const Ufe::Path& pulledPath) : Ufe::Hierarchy() - , _mayaHierarchyHandler(mayaHierarchyHandler) - , _mayaHierarchy(_mayaHierarchyHandler->hierarchy(item)) + , _mayaHierarchy(mayaHierarchyHandler->hierarchy(item)) , _pulledPath(pulledPath) { } @@ -82,26 +81,13 @@ PulledObjectHierarchy::insertChild(const Ufe::SceneItem::Ptr& child, const Ufe:: return nullptr; } -Ufe::SceneItem::Ptr PulledObjectHierarchy::createGroup( -#if (UFE_PREVIEW_VERSION_NUM < 3005) - const Ufe::Selection& selection, -#endif - const Ufe::PathComponent& name) const +Ufe::SceneItem::Ptr PulledObjectHierarchy::createGroup(const Ufe::PathComponent& name) const { TF_CODING_ERROR("Illegal call to unimplemented %s", __func__); return nullptr; } -#if (UFE_PREVIEW_VERSION_NUM >= 3001) -Ufe::InsertChildCommand::Ptr -#else -Ufe::UndoableCommand::Ptr -#endif -PulledObjectHierarchy::createGroupCmd( -#if (UFE_PREVIEW_VERSION_NUM < 3005) - const Ufe::Selection& selection, -#endif - const Ufe::PathComponent& name) const +Ufe::InsertChildCommand::Ptr PulledObjectHierarchy::createGroupCmd(const Ufe::PathComponent& name) const { TF_CODING_ERROR("Illegal call to unimplemented %s", __func__); return nullptr; @@ -114,13 +100,11 @@ PulledObjectHierarchy::reorderCmd(const Ufe::SceneItemList& orderedList) const return nullptr; } -#ifdef UFE_V3_FEATURES_AVAILABLE Ufe::UndoableCommand::Ptr PulledObjectHierarchy::ungroupCmd() const { TF_CODING_ERROR("Illegal call to unimplemented %s", __func__); return nullptr; } -#endif // UFE_V3_FEATURES_AVAILABLE Ufe::SceneItem::Ptr PulledObjectHierarchy::defaultParent() const { diff --git a/lib/mayaUsd/ufe/PulledObjectHierarchy.h b/lib/mayaUsd/ufe/PulledObjectHierarchy.h index 70368b6854..020925421b 100644 --- a/lib/mayaUsd/ufe/PulledObjectHierarchy.h +++ b/lib/mayaUsd/ufe/PulledObjectHierarchy.h @@ -1,5 +1,5 @@ // -// Copyright 2019 Autodesk +// 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. @@ -23,9 +23,14 @@ namespace MAYAUSD_NS_DEF { namespace ufe { -//! \brief TODOC +//! \brief Maya hierarchy interface for pulled Maya objects. /*! - TODOC + See PulledObjectHierarchyHandler for pulled object data model details. + The hierarchy interface of the pulled sub-hierarchy root is a + PulledObjectHierarchy object. Its children are the normal Maya children; + its parent is the USD parent of the pulled prim. This allows the pulled + Maya data to respond correctly to hierarchy viewing (e.g. the Outliner) and + navigation (e.g. pick walking). */ class MAYAUSD_CORE_PUBLIC PulledObjectHierarchy : public Ufe::Hierarchy { @@ -56,21 +61,9 @@ class MAYAUSD_CORE_PUBLIC PulledObjectHierarchy : public Ufe::Hierarchy Ufe::SceneItemList filteredChildren(const ChildFilter&) const override; Ufe::SceneItem::Ptr parent() const override; - Ufe::SceneItem::Ptr createGroup( -#if (UFE_PREVIEW_VERSION_NUM < 3005) - const Ufe::Selection& selection, -#endif - const Ufe::PathComponent& name) const override; -#if (UFE_PREVIEW_VERSION_NUM >= 3001) - Ufe::InsertChildCommand::Ptr -#else - Ufe::UndoableCommand::Ptr -#endif - createGroupCmd( -#if (UFE_PREVIEW_VERSION_NUM < 3005) - const Ufe::Selection& selection, -#endif - const Ufe::PathComponent& name) const override; + Ufe::SceneItem::Ptr createGroup(const Ufe::PathComponent& name) const override; + + Ufe::InsertChildCommand::Ptr createGroupCmd(const Ufe::PathComponent& name) const override; Ufe::SceneItem::Ptr defaultParent() const override; @@ -81,12 +74,9 @@ class MAYAUSD_CORE_PUBLIC PulledObjectHierarchy : public Ufe::Hierarchy Ufe::UndoableCommand::Ptr reorderCmd(const Ufe::SceneItemList& orderedList) const override; -#ifdef UFE_V3_FEATURES_AVAILABLE Ufe::UndoableCommand::Ptr ungroupCmd() const override; -#endif private: - Ufe::HierarchyHandler::Ptr _mayaHierarchyHandler; Hierarchy::Ptr _mayaHierarchy; Ufe::Path _pulledPath; }; // PulledObjectHierarchy diff --git a/lib/mayaUsd/ufe/PulledObjectHierarchyHandler.cpp b/lib/mayaUsd/ufe/PulledObjectHierarchyHandler.cpp index e600ad347a..795416ceb5 100644 --- a/lib/mayaUsd/ufe/PulledObjectHierarchyHandler.cpp +++ b/lib/mayaUsd/ufe/PulledObjectHierarchyHandler.cpp @@ -1,5 +1,5 @@ // -// Copyright 2019 Autodesk +// 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. @@ -15,7 +15,7 @@ // #include "PulledObjectHierarchyHandler.h" -#include +#include #include #include @@ -57,7 +57,7 @@ Ufe::Hierarchy::Ptr PulledObjectHierarchyHandler::hierarchy(const Ufe::SceneItem Ufe::Path ufePath; auto dagPath = PXR_NS::UsdMayaUtil::nameToDagPath(path.getSegments()[nbSegs - 1].string()); - if (PXR_NS::UsdMayaPrimUpdater::readPullInformation(dagPath, ufePath)) { + if (PXR_NS::PrimUpdaterManager::readPullInformation(dagPath, ufePath)) { return PulledObjectHierarchy::create(_mayaHierarchyHandler, item, ufePath); } else { return _mayaHierarchyHandler->hierarchy(item); diff --git a/lib/mayaUsd/ufe/PulledObjectHierarchyHandler.h b/lib/mayaUsd/ufe/PulledObjectHierarchyHandler.h index d3b838b762..f870695931 100644 --- a/lib/mayaUsd/ufe/PulledObjectHierarchyHandler.h +++ b/lib/mayaUsd/ufe/PulledObjectHierarchyHandler.h @@ -1,5 +1,5 @@ // -// Copyright 2019 Autodesk +// 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. @@ -24,7 +24,18 @@ namespace ufe { //! \brief Maya run-time hierarchy handler with support for pulled Maya objects. /*! - TO DOC + Pulled Maya objects are a sub-hierarchy of USD objects that are being + edited as Maya data. In Maya form, the sub-hierarchy is still rooted to + its USD parent through pull information on the Maya root of the pulled + sub-hierarchy. + + The PulledObjectHierarchyHandler wraps its argument Maya hierarchy handler, + and calls it for scene item creation. For hierarchy interface creation, + the PulledObjectHierarchyHandler will check the Maya Dag path if there is + pull information associated with it, which will be the case for the root of + the pulled sub-hierarchy. If so, it will create a PulledObjectHierarchy + interface. If not, it will delegate to the Maya hierarchy handler, which + will create a normal Maya hierarchy interface. */ class MAYAUSD_CORE_PUBLIC PulledObjectHierarchyHandler : public Ufe::HierarchyHandler { diff --git a/lib/mayaUsd/ufe/UsdHierarchy.cpp b/lib/mayaUsd/ufe/UsdHierarchy.cpp index b9c6cb520f..b40d39b62c 100644 --- a/lib/mayaUsd/ufe/UsdHierarchy.cpp +++ b/lib/mayaUsd/ufe/UsdHierarchy.cpp @@ -45,7 +45,7 @@ #endif #ifdef UFE_V3_FEATURES_AVAILABLE -#include +#include #include #include // In UFE v2 but only needed for primUpdater. @@ -146,7 +146,7 @@ Ufe::SceneItemList UsdHierarchy::createUFEChildList(const UsdPrimSiblingRange& r UFE_V3(std::string dagPathStr;) for (const auto& child : range) { #ifdef UFE_V3_FEATURES_AVAILABLE - if (PXR_NS::UsdMayaPrimUpdater::readPullInformation(child, dagPathStr)) { + if (PXR_NS::PrimUpdaterManager::readPullInformation(child, dagPathStr)) { auto item = Ufe::Hierarchy::createItem(Ufe::PathString::path(dagPathStr)); if (TF_VERIFY(item)) { children.emplace_back(item); diff --git a/lib/mayaUsd/ufe/UsdPathMappingHandler.cpp b/lib/mayaUsd/ufe/UsdPathMappingHandler.cpp index d9a88e3a26..7e1eb34a34 100644 --- a/lib/mayaUsd/ufe/UsdPathMappingHandler.cpp +++ b/lib/mayaUsd/ufe/UsdPathMappingHandler.cpp @@ -17,7 +17,7 @@ #include "UsdSceneItem.h" -#include +#include #include #include @@ -111,7 +111,7 @@ Ufe::Path UsdPathMappingHandler::fromHost(const Ufe::Path& hostPath) const mayaComps.emplace_back(mayaHostPath.back()); mayaHostPath = mayaHostPath.pop(); Ufe::Path ufePath; - if (PXR_NS::UsdMayaPrimUpdater::readPullInformation(dagPath, ufePath)) { + if (PXR_NS::PrimUpdaterManager::readPullInformation(dagPath, ufePath)) { // From the pulled info path, we pop only the last component and replace it with // the Maya component array. std::reverse(mayaComps.begin(), mayaComps.end()); diff --git a/lib/usd/translators/mayaReferenceUpdater.cpp b/lib/usd/translators/mayaReferenceUpdater.cpp index 0ae8617e29..9806177df1 100644 --- a/lib/usd/translators/mayaReferenceUpdater.cpp +++ b/lib/usd/translators/mayaReferenceUpdater.cpp @@ -60,7 +60,7 @@ PxrUsdTranslators_MayaReferenceUpdater::PxrUsdTranslators_MayaReferenceUpdater( } /* virtual */ -bool PxrUsdTranslators_MayaReferenceUpdater::PushCopySpecs( +bool PxrUsdTranslators_MayaReferenceUpdater::pushCopySpecs( SdfLayerRefPtr srcLayer, const SdfPath& srcSdfPath, SdfLayerRefPtr dstLayer, @@ -99,12 +99,12 @@ bool PxrUsdTranslators_MayaReferenceUpdater::PushCopySpecs( } /* virtual */ -bool PxrUsdTranslators_MayaReferenceUpdater::DiscardEdits(const UsdMayaPrimUpdaterContext& context) +bool PxrUsdTranslators_MayaReferenceUpdater::discardEdits(const UsdMayaPrimUpdaterContext& context) { - const MObject& parentNode = GetMayaObject(); + const MObject& parentNode = getMayaObject(); UsdMayaTranslatorMayaReference::UnloadMayaReference(parentNode); - return UsdMayaPrimUpdater::DiscardEdits(context); + return UsdMayaPrimUpdater::discardEdits(context); } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/usd/translators/mayaReferenceUpdater.h b/lib/usd/translators/mayaReferenceUpdater.h index d45386d259..1f163c9b3b 100644 --- a/lib/usd/translators/mayaReferenceUpdater.h +++ b/lib/usd/translators/mayaReferenceUpdater.h @@ -35,11 +35,11 @@ class PxrUsdTranslators_MayaReferenceUpdater : public UsdMayaPrimUpdater const Ufe::Path& path); MAYAUSD_CORE_PUBLIC - bool DiscardEdits(const UsdMayaPrimUpdaterContext& context) override; + bool discardEdits(const UsdMayaPrimUpdaterContext& context) override; protected: MAYAUSD_CORE_PUBLIC - bool PushCopySpecs( + bool pushCopySpecs( SdfLayerRefPtr srcLayer, const SdfPath& srcSdfPath, SdfLayerRefPtr dstLayer, diff --git a/plugin/adsk/plugin/plugin.cpp b/plugin/adsk/plugin/plugin.cpp index ddfedb5cdd..969af5d0ff 100644 --- a/plugin/adsk/plugin/plugin.cpp +++ b/plugin/adsk/plugin/plugin.cpp @@ -328,7 +328,7 @@ MStatus initializePlugin(MObject obj) #ifdef UFE_V3_FEATURES_AVAILABLE // Install notifications - PrimUpdaterManager::GetInstance(); + PrimUpdaterManager::getInstance(); #endif return status; From 02b707913843a3ce1d10d0310e864a3bb0865dac Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Mon, 1 Nov 2021 18:19:33 -0400 Subject: [PATCH 08/12] clang-format --- lib/mayaUsd/fileio/primUpdaterManager.h | 1 + lib/mayaUsd/ufe/PulledObjectHierarchy.cpp | 3 ++- lib/mayaUsd/ufe/PulledObjectHierarchy.h | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/mayaUsd/fileio/primUpdaterManager.h b/lib/mayaUsd/fileio/primUpdaterManager.h index 26447ce2d5..0b635345e4 100644 --- a/lib/mayaUsd/fileio/primUpdaterManager.h +++ b/lib/mayaUsd/fileio/primUpdaterManager.h @@ -64,6 +64,7 @@ class PrimUpdaterManager : public PXR_NS::TfWeakBase static bool readPullInformation(const Ufe::Path& ufePath, MDagPath& dagPath); MAYAUSD_CORE_PUBLIC static bool readPullInformation(const MDagPath& dagpath, Ufe::Path& ufePath); + private: PrimUpdaterManager(); diff --git a/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp b/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp index 9fd189e39c..67f55edfb5 100644 --- a/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp +++ b/lib/mayaUsd/ufe/PulledObjectHierarchy.cpp @@ -87,7 +87,8 @@ Ufe::SceneItem::Ptr PulledObjectHierarchy::createGroup(const Ufe::PathComponent& return nullptr; } -Ufe::InsertChildCommand::Ptr PulledObjectHierarchy::createGroupCmd(const Ufe::PathComponent& name) const +Ufe::InsertChildCommand::Ptr +PulledObjectHierarchy::createGroupCmd(const Ufe::PathComponent& name) const { TF_CODING_ERROR("Illegal call to unimplemented %s", __func__); return nullptr; diff --git a/lib/mayaUsd/ufe/PulledObjectHierarchy.h b/lib/mayaUsd/ufe/PulledObjectHierarchy.h index 020925421b..ba7101e0e5 100644 --- a/lib/mayaUsd/ufe/PulledObjectHierarchy.h +++ b/lib/mayaUsd/ufe/PulledObjectHierarchy.h @@ -77,8 +77,8 @@ class MAYAUSD_CORE_PUBLIC PulledObjectHierarchy : public Ufe::Hierarchy Ufe::UndoableCommand::Ptr ungroupCmd() const override; private: - Hierarchy::Ptr _mayaHierarchy; - Ufe::Path _pulledPath; + Hierarchy::Ptr _mayaHierarchy; + Ufe::Path _pulledPath; }; // PulledObjectHierarchy } // namespace ufe From ffec2dc867067f82420db268643bc34bc2707e4e Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Tue, 2 Nov 2021 16:18:39 -0400 Subject: [PATCH 09/12] Duplicate from Maya to USD and vice versa, with new wording in menus. --- lib/mayaUsd/fileio/primUpdaterManager.cpp | 98 ++++++++++------------- lib/mayaUsd/ufe/UsdContextOps.cpp | 8 +- plugin/adsk/scripts/USDMenuProc.mel | 2 +- 3 files changed, 49 insertions(+), 59 deletions(-) diff --git a/lib/mayaUsd/fileio/primUpdaterManager.cpp b/lib/mayaUsd/fileio/primUpdaterManager.cpp index 6f8eaee1cc..fc0341fd71 100644 --- a/lib/mayaUsd/fileio/primUpdaterManager.cpp +++ b/lib/mayaUsd/fileio/primUpdaterManager.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -257,7 +258,7 @@ bool removeExcludeFromRendering(const Ufe::Path& ufePulledPath) // the prim refer to the same object: the prim is passed in as an // optimization to avoid an additional call to ufePathToPrim(). using PullImportPaths = std::pair, std::vector>; -PullImportPaths PullImport( +PullImportPaths pullImport( const Ufe::Path& ufePulledPath, const UsdPrim& pulledPrim, const UsdMayaPrimUpdaterContext& context) @@ -352,7 +353,7 @@ PullImportPaths PullImport( //------------------------------------------------------------------------------ // // Perform the customization step of the pull (second step). -bool PullCustomize(const PullImportPaths& importedPaths, const UsdMayaPrimUpdaterContext& context) +bool pullCustomize(const PullImportPaths& importedPaths, const UsdMayaPrimUpdaterContext& context) { TF_AXIOM(importedPaths.first.size() == importedPaths.second.size()); auto dagPathIt = importedPaths.first.begin(); @@ -388,7 +389,7 @@ using UsdPathToDagPathMap = TfHashMap; using UsdPathToDagPathMapPtr = std::shared_ptr; using PushCustomizeSrc = std::tuple; -PushCustomizeSrc PushExport( +PushCustomizeSrc pushExport( const Ufe::Path& ufePulledPath, const MObject& mayaObject, const UsdMayaPrimUpdaterContext& context) @@ -518,17 +519,17 @@ UsdMayaPrimUpdaterSharedPtr createUpdater( // Perform the customization step of the merge to USD (second step). Traverse // the in-memory layer, creating a prim updater for each prim, and call Push // for each updater. -bool PushCustomize( +bool pushCustomize( const Ufe::Path& ufePulledPath, const PushCustomizeSrc& src, const UsdMayaPrimUpdaterContext& context) { const auto& srcRootPath = std::get(src); - const auto& srcLayer = std::get(src); if (srcRootPath.IsEmpty()) { return false; } + const auto& srcLayer = std::get(src); const bool isCopy = context.GetArgs()._copyOperation; const auto& editTarget = context.GetUsdStage()->GetEditTarget(); @@ -714,7 +715,7 @@ bool PrimUpdaterManager::push(const MFnDependencyNode& depNodeFn, const Ufe::Pat // per-prim customization. // 1) Perform the export to the temporary layer. - auto pushCustomizeSrc = PushExport(pulledPath, depNodeFn.object(), context); + auto pushCustomizeSrc = pushExport(pulledPath, depNodeFn.object(), context); // 2) Traverse the in-memory layer, creating a prim updater for each prim, // and call Push for each updater. Build a new context with the USD path @@ -725,7 +726,7 @@ bool PrimUpdaterManager::push(const MFnDependencyNode& depNodeFn, const Ufe::Pat exportArgs, std::get(pushCustomizeSrc)); - if (!PushCustomize(pulledPath, pushCustomizeSrc, customizeContext)) { + if (!pushCustomize(pulledPath, pushCustomizeSrc, customizeContext)) { return false; } @@ -780,10 +781,10 @@ bool PrimUpdaterManager::pull(const Ufe::Path& path) // each, for per-prim customization. // 1) Perform the import - PullImportPaths importedPaths = PullImport(path, pulledPrim, context); + PullImportPaths importedPaths = pullImport(path, pulledPrim, context); // 2) Iterate over all imported Dag paths. - if (!PullCustomize(importedPaths, context)) { + if (!pullCustomize(importedPaths, context)) { return false; } @@ -860,77 +861,66 @@ bool PrimUpdaterManager::copyBetween(const Ufe::Path& srcPath, const Ufe::Path& MayaUsdProxyShapeBase* dstProxyShape = MayaUsd::ufe::getProxyShape(dstPath); PushPullScope scopeIt(_inPushPull); - bool ret = false; // Copy from USD to DG if (srcProxyShape && dstProxyShape == nullptr) { - SdfPath srcSdfPath = ufeToSdfPath(srcPath); - if (srcSdfPath.IsEmpty()) { + auto srcPrim = MayaUsd::ufe::ufePathToPrim(srcPath); + if (!srcPrim) { return false; } - MFnDependencyNode depNodeFn; - - UsdStageRefPtr proxyStage = srcProxyShape->usdPrim().GetStage(); - UsdPrim srcPrim = proxyStage->GetPrimAtPath(srcSdfPath); - TfToken typeName = srcPrim.GetTypeName(); - - auto registryItem = UsdMayaPrimUpdaterRegistry::FindOrFallback(typeName); - auto factory = std::get(registryItem); - UsdMayaPrimUpdaterSharedPtr updater = factory(depNodeFn, srcPath); - VtDictionary userArgs = UsdMayaJobImportArgs::GetDefaultDictionary(); // We will only do copy between two data models, setting this in arguments // to configure the updater userArgs[UsdMayaPrimUpdaterArgsTokens->copyOperation] = true; - UsdMayaPrimUpdaterContext context(srcProxyShape->getTime(), proxyStage, userArgs); + UsdMayaPrimUpdaterContext context( + srcProxyShape->getTime(), srcProxyShape->getUsdStage(), userArgs); - // FIXME Re-implement with proper traversal. PPT, 22-Oct-2021. -#if 0 - ret = updater->Pull(context); -#endif + pullImport(srcPath, srcPrim, context); + return true; } // Copy from DG to USD else if (srcProxyShape == nullptr && dstProxyShape) { - // Remove the leading "|world| component. - MDagPath dagPath - = PXR_NS::UsdMayaUtil::nameToDagPath(srcPath.getSegments()[0].popHead().string()); - MFnDependencyNode dgNodeFn(dagPath.node()); - - const std::string mayaTypeName(dgNodeFn.typeName().asChar()); - - auto registryItem = UsdMayaPrimUpdaterRegistry::FindOrFallback(mayaTypeName); - auto factory = std::get(registryItem); - UsdMayaPrimUpdaterSharedPtr updater = factory(dgNodeFn, dstPath); - + TF_AXIOM(srcPath.nbSegments() == 1); + MDagPath dagPath = PXR_NS::UsdMayaUtil::nameToDagPath(Ufe::PathString::string(srcPath)); + if (!dagPath.isValid()) { + return false; + } VtDictionary userArgs = UsdMayaJobExportArgs::GetDefaultDictionary(); // We will only do copy between two data models, setting this in arguments // to configure the updater userArgs[UsdMayaPrimUpdaterArgsTokens->copyOperation] = true; + auto dstStage = dstProxyShape->getUsdStage(); + UsdMayaPrimUpdaterContext context(dstProxyShape->getTime(), dstStage, userArgs); - UsdMayaPrimUpdaterContext context( - dstProxyShape->getTime(), dstProxyShape->usdPrim().GetStage(), userArgs); + // Export out to a temporary layer. + auto pushExportOutput = pushExport(srcPath, dagPath.node(), context); + const auto& srcRootPath = std::get(pushExportOutput); + if (srcRootPath.IsEmpty()) { + return false; + } - // FIXME Re-implement with proper traversal. PPT, 22-Oct-2021. -#if 0 - ret = updater->Push(context); -#endif + // Copy the temporary layer contents out to the proper destination. + const auto& srcLayer = std::get(pushExportOutput); + const auto& editTarget = dstStage->GetEditTarget(); + auto dstRootPath = editTarget.MapToSpecPath(srcRootPath); + const auto& dstLayer = editTarget.GetLayer(); - if (ret) { - auto ufeItem = Ufe::Hierarchy::createItem(dstPath); - if (TF_VERIFY(ufeItem)) { - Ufe::Scene::instance().notify(Ufe::SubtreeInvalidate(ufeItem)); - } + if (!SdfCopySpec(srcLayer, srcRootPath, dstLayer, dstRootPath)) { + return false; } - } - // We don't perform copy operations in the same data model in here. - else { - return false; + + auto ufeItem = Ufe::Hierarchy::createItem(dstPath); + if (TF_VERIFY(ufeItem)) { + Ufe::Scene::instance().notify(Ufe::SubtreeInvalidate(ufeItem)); + } + return true; } - return ret; + // Copy operations to the same data model not supported here. + return false; } void PrimUpdaterManager::onProxyContentChanged( diff --git a/lib/mayaUsd/ufe/UsdContextOps.cpp b/lib/mayaUsd/ufe/UsdContextOps.cpp index c8fe2db753..4c64b7c6f9 100644 --- a/lib/mayaUsd/ufe/UsdContextOps.cpp +++ b/lib/mayaUsd/ufe/UsdContextOps.cpp @@ -108,8 +108,8 @@ static const std::string kUSDSpherePrimImage { "out_USD_Sphere.png" }; static constexpr char kEditAsMayaItem[] = "Edit As Maya Data"; static constexpr char kEditAsMayaLabel[] = "Edit As Maya Data"; static const std::string kEditAsMayaImage { "edit_as_Maya.png" }; -static constexpr char kCopyAsMayaItem[] = "Copy As Maya Data"; -static constexpr char kCopyAsMayaLabel[] = "Copy As Maya Data"; +static constexpr char kDuplicateAsMayaItem[] = "Duplicate As Maya Data"; +static constexpr char kDuplicateAsMayaLabel[] = "Duplicate As Maya Data"; #if PXR_VERSION >= 2008 static constexpr char kAllRegisteredTypesItem[] = "All Registered"; @@ -574,7 +574,7 @@ Ufe::ContextOps::Items UsdContextOps::getItems(const Ufe::ContextOps::ItemPath& // Top-level items (do not add for gateway type node): if (!fIsAGatewayType) { items.emplace_back(kEditAsMayaItem, kEditAsMayaLabel, kEditAsMayaImage); - items.emplace_back(kCopyAsMayaItem, kCopyAsMayaLabel); + items.emplace_back(kDuplicateAsMayaItem, kDuplicateAsMayaLabel); items.emplace_back(Ufe::ContextItem::kSeparator); // Working set management (load and unload): @@ -783,7 +783,7 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) MString script; script.format("mayaUsdMenu_pullToDG \"^1s\"", Ufe::PathString::string(path()).c_str()); MGlobal::executeCommand(script); - } else if (itemPath[0] == kCopyAsMayaItem) { + } else if (itemPath[0] == kDuplicateAsMayaItem) { MString script; script.format("mayaUsdMenu_copyToDG \"^1s\"", Ufe::PathString::string(path()).c_str()); MGlobal::executeCommand(script); diff --git a/plugin/adsk/scripts/USDMenuProc.mel b/plugin/adsk/scripts/USDMenuProc.mel index 9755a84ba7..735e26a154 100644 --- a/plugin/adsk/scripts/USDMenuProc.mel +++ b/plugin/adsk/scripts/USDMenuProc.mel @@ -45,6 +45,6 @@ global proc USDMenuProc(string $parent, string $obj) setParent -menu ..; menuItem -label "Edit As Maya Data" -image "edit_as_Maya.png" -command ("mayaUsdMenu_pullToDG \"" + $obj + "\""); - menuItem -label "Copy As Maya Data" -command ("mayaUsdMenu_copyToDG \"" + $obj + "\""); + menuItem -label "Duplicate As Maya Data" -command ("mayaUsdMenu_copyToDG \"" + $obj + "\""); } } From acd10e47e818fadbbca7e5da891aeea3b2ed8e74 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Wed, 3 Nov 2021 11:33:07 -0400 Subject: [PATCH 10/12] Fix for on-exit macOS failure, and addressed code review feedback. --- lib/mayaUsd/ufe/Global.cpp | 8 +++++++- lib/usd/translators/mayaReferenceUpdater.cpp | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/mayaUsd/ufe/Global.cpp b/lib/mayaUsd/ufe/Global.cpp index f8af937a3a..f5142bbd3e 100644 --- a/lib/mayaUsd/ufe/Global.cpp +++ b/lib/mayaUsd/ufe/Global.cpp @@ -85,6 +85,10 @@ Ufe::HierarchyHandler::Ptr g_MayaHierarchyHandler; Ufe::ContextOpsHandler::Ptr g_MayaContextOpsHandler; #endif +#ifdef HAVE_PATH_MAPPING +Ufe::PathMappingHandler::Ptr g_MayaPathMappingHandler; +#endif + // Subject singleton for observation of all USD stages. StagesSubject::Ptr g_StagesSubject; @@ -164,6 +168,7 @@ MStatus initialize() MayaUsd::ufe::UsdUIUfeObserver::create(); #ifdef HAVE_PATH_MAPPING + g_MayaPathMappingHandler = Ufe::RunTimeMgr::instance().pathMappingHandler(g_MayaRtid); auto pathMappingHndlr = UsdPathMappingHandler::create(); Ufe::RunTimeMgr::instance().setPathMappingHandler(g_MayaRtid, pathMappingHndlr); #endif @@ -211,7 +216,8 @@ MStatus finalize() #ifdef HAVE_PATH_MAPPING // Remove the Maya path mapping handler that we added above. - Ufe::RunTimeMgr::instance().setPathMappingHandler(g_MayaRtid, nullptr); + Ufe::RunTimeMgr::instance().setPathMappingHandler(g_MayaRtid, g_MayaPathMappingHandler); + g_MayaPathMappingHandler.reset(); #endif g_StagesSubject.Reset(); diff --git a/lib/usd/translators/mayaReferenceUpdater.cpp b/lib/usd/translators/mayaReferenceUpdater.cpp index 9806177df1..fe11034e8a 100644 --- a/lib/usd/translators/mayaReferenceUpdater.cpp +++ b/lib/usd/translators/mayaReferenceUpdater.cpp @@ -68,6 +68,7 @@ bool PxrUsdTranslators_MayaReferenceUpdater::pushCopySpecs( { bool success = false; + // Prototype code, subject to change shortly. PPT, 3-Nov-2021. // We are looking for a very specific configuration in here // i.e. a parent prim with a variant set called "animVariant" // and two variants "cache" and "rig" From 2eb60f27baf44bcce3e83c5f719e54dc6cf72d87 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Thu, 4 Nov 2021 15:38:22 -0400 Subject: [PATCH 11/12] UFE registration cleanup on program exit. --- lib/mayaUsd/ufe/Global.cpp | 20 ++++++++++++++++++-- lib/mayaUsd/ufe/Global.h | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/mayaUsd/ufe/Global.cpp b/lib/mayaUsd/ufe/Global.cpp index f5142bbd3e..229acc93bc 100644 --- a/lib/mayaUsd/ufe/Global.cpp +++ b/lib/mayaUsd/ufe/Global.cpp @@ -44,6 +44,8 @@ #include #endif +#include + #include #include #ifdef UFE_V2_FEATURES_AVAILABLE @@ -54,7 +56,17 @@ #include namespace { + +void exitingCallback(void* /* unusedData */) { + // Maya does not unload plugins on exit. Make sure we perform an orderly + // cleanup, otherwise on program exit UFE static data structures may be + // cleaned up when this plugin is no longer alive. + MayaUsd::ufe::finalize(/* exiting = */ true); +} + int gRegistrationCount = 0; + +bool gExitingCbId = 0; } namespace MAYAUSD_NS_DEF { @@ -192,13 +204,15 @@ MStatus initialize() // Register for UFE string to path service using path component separator '/' UFE_V2(Ufe::PathString::registerPathComponentSeparator(g_USDRtid, '/');) + gExitingCbId = MSceneMessage::addCallback(MSceneMessage::kMayaExiting, exitingCallback); + return MS::kSuccess; } -MStatus finalize() +MStatus finalize(bool exiting) { // If more than one plugin still has us registered, do nothing. - if (gRegistrationCount-- > 1) + if (gRegistrationCount-- > 1 && !exiting) return MS::kSuccess; // Restore the normal Maya hierarchy handler, and unregister. @@ -222,6 +236,8 @@ MStatus finalize() g_StagesSubject.Reset(); + MMessage::removeCallback(gExitingCbId); + return MS::kSuccess; } diff --git a/lib/mayaUsd/ufe/Global.h b/lib/mayaUsd/ufe/Global.h index b6035aea01..56948a6bc4 100644 --- a/lib/mayaUsd/ufe/Global.h +++ b/lib/mayaUsd/ufe/Global.h @@ -31,7 +31,7 @@ MStatus initialize(); //! Only intended to be called by the plugin finalization, to //! finalize the handlers stage model. MAYAUSD_CORE_PUBLIC -MStatus finalize(); +MStatus finalize(bool exiting = false); //! Return the run-time ID allocated to USD. MAYAUSD_CORE_PUBLIC From c3c8d8fb033d180ca153334300988d88192b4413 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Thu, 4 Nov 2021 17:10:41 -0400 Subject: [PATCH 12/12] clang-format --- lib/mayaUsd/ufe/Global.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/mayaUsd/ufe/Global.cpp b/lib/mayaUsd/ufe/Global.cpp index 229acc93bc..b3943841cf 100644 --- a/lib/mayaUsd/ufe/Global.cpp +++ b/lib/mayaUsd/ufe/Global.cpp @@ -45,7 +45,6 @@ #endif #include - #include #include #ifdef UFE_V2_FEATURES_AVAILABLE @@ -57,7 +56,8 @@ namespace { -void exitingCallback(void* /* unusedData */) { +void exitingCallback(void* /* unusedData */) +{ // Maya does not unload plugins on exit. Make sure we perform an orderly // cleanup, otherwise on program exit UFE static data structures may be // cleaned up when this plugin is no longer alive. @@ -67,7 +67,7 @@ void exitingCallback(void* /* unusedData */) { int gRegistrationCount = 0; bool gExitingCbId = 0; -} +} // namespace namespace MAYAUSD_NS_DEF { namespace ufe {