Skip to content

Commit

Permalink
Merge pull request #2245 from Autodesk/t_bailp/MAYA-122369/missing-ed…
Browse files Browse the repository at this point in the history
…its-after-merge

MAYA-122369 fix missing edit after merge
  • Loading branch information
seando-adsk authored Apr 14, 2022
2 parents 5f6a387 + 6a41420 commit 6f3a92b
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 8 deletions.
12 changes: 12 additions & 0 deletions lib/usd/utils/MergePrims.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include <pxr/base/tf/stringUtils.h>
#include <pxr/usd/sdf/copyUtils.h>
#include <pxr/usd/usd/editContext.h>
#include <pxr/usd/usdGeom/xformCommonAPI.h>

#include <algorithm>
Expand Down Expand Up @@ -789,6 +790,15 @@ bool mergeDiffPrims(
return SdfCopySpec(srcLayer, srcPath, dstLayer, dstPath, copyValue, copyChildren);
}

//----------------------------------------------------------------------------------------------------------------------
/// Create any missing parents as "over". Parents may be missing because we are targeting a
/// different layer than where the destination prim is authored. The SdfCopySpec function does not
/// automatically create the missing parent, unlike other functions like UsdStage::CreatePrim.
void createMissingParents(const SdfLayerRefPtr& dstLayer, const SdfPath& dstPath)
{
SdfJustCreatePrimInLayer(dstLayer, dstPath.GetParentPath());
}

} // namespace

//----------------------------------------------------------------------------------------------------------------------
Expand All @@ -806,6 +816,8 @@ bool mergePrims(
const SdfPath& dstPath,
const MergePrimsOptions& options)
{
createMissingParents(dstLayer, dstPath);

if (options.ignoreUpperLayerOpinions) {
auto tempStage = UsdStage::CreateInMemory();
SdfLayerHandle tempLayer = tempStage->GetSessionLayer();
Expand Down
43 changes: 42 additions & 1 deletion test/lib/mayaUsd/fileio/testMergeToUsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import fixturesUtils

from mayaUtils import setMayaTranslation, setMayaRotation
from usdUtils import createSimpleXformScene, mayaUsd_createStageWithNewLayer
from usdUtils import createSimpleXformScene, mayaUsd_createStageWithNewLayer, createLayeredStage, createSimpleXformSceneInCurrentLayer
from ufeUtils import ufeFeatureSetVersion

import mayaUsd.lib
Expand Down Expand Up @@ -257,6 +257,47 @@ def testMergeToUsdToNonRootTargetInSessionLayer(self):

assertVectorAlmostEqual(self, mayaValues, usdValues)

@unittest.skipUnless(ufeFeatureSetVersion() >= 3, 'Test only available in UFE v3 or greater.')
def testMergeToUsdToParentLayer(self):
'''Merge edits on a USD transform back to USD targeting a parent layer.'''

# Create a multi-layered scene with prim on the lowest layer.
# We should receive three layers: root and two additional sub-layers.
(psPathStr, psPath, ps, layers) = createLayeredStage(2)
self.assertEqual(3, len(layers))

stage = mayaUsd.lib.GetPrim(psPathStr).GetStage()
stage.SetEditTarget(layers[-1])

(aXlateOp, aXlation, aUsdUfePathStr, aUsdUfePath, aUsdItem,
bXlateOp, bXlation, bUsdUfePathStr, bUsdUfePath, bUsdItem) = createSimpleXformSceneInCurrentLayer(psPathStr, ps)

# To merge back to USD, we must edit as Maya first.
with mayaUsd.lib.OpUndoItemList():
self.assertTrue(mayaUsd.lib.PrimUpdaterManager.editAsMaya(bUsdUfePathStr))

bMayaItem = ufe.GlobalSelection.get().front()
(bMayaPath, bMayaPathStr, _, bMayaMatrix) = \
setMayaTranslation(bMayaItem, om.MVector(10, 11, 12))

psHier = ufe.Hierarchy.hierarchy(ps)

# Before merging, set the edit target to the top non-root layer
stage = mayaUsd.lib.GetPrim(psPathStr).GetStage()
stage.SetEditTarget(layers[1])

# Merge edits back to USD.
with mayaUsd.lib.OpUndoItemList():
self.assertTrue(mayaUsd.lib.PrimUpdaterManager.mergeToUsd(bMayaPathStr))

# Check that edits have been preserved in USD.
bUsdMatrix = bXlateOp.GetOpTransform(
mayaUsd.ufe.getTime(bUsdUfePathStr))
mayaValues = [v for v in bMayaMatrix]
usdValues = [v for row in bUsdMatrix for v in row]

assertVectorAlmostEqual(self, mayaValues, usdValues)

def testEquivalentTransformMergeToUsd(self):
'''Merge edits on a USD transform back to USD when the new transform is equivalent.'''

Expand Down
78 changes: 71 additions & 7 deletions test/testUtils/usdUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import ufe
import ufeUtils

from pxr import Usd, UsdGeom, Gf
from pxr import Usd, UsdGeom, Gf, Sdf

usdSeparator = '/'

Expand Down Expand Up @@ -83,17 +83,32 @@ def createAnimatedHierarchy(stage):
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:
def createSimpleStage():
'''Create a simple stage and layer:
Returns a tuple of:
- proxy shape UFE path string
- proxy shape UFE path
- proxy shape UFE item
'''
psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer()
psPath = ufe.PathString.path(psPathStr)
ps = ufe.Hierarchy.createItem(psPath)
return (psPathStr, psPath, ps)

def createSimpleXformSceneInCurrentLayer(psPathStr, ps):
'''Create a simple scene in the current stage and layer with a trivial hierarchy:
A translation (1, 2, 3)
|_B translation (7, 8, 9)
Returns a tuple of:
- A translation op, A translation vector
- A UFE path string, A UFE path, A UFE item
- B translation op, B translation vector
- B UFE path string, B UFE path, B UFE item
'''

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)
Expand All @@ -113,5 +128,54 @@ def createSimpleXformScene():
bUsdUfePath = ufe.PathString.path(bUsdUfePathStr)
bUsdItem = ufe.Hierarchy.createItem(bUsdUfePath)

return (ps, aXlateOp, aXlation, aUsdUfePathStr, aUsdUfePath, aUsdItem,
return (aXlateOp, aXlation, aUsdUfePathStr, aUsdUfePath, aUsdItem,
bXlateOp, bXlation, bUsdUfePathStr, bUsdUfePath, bUsdItem)

def createSimpleXformScene():
'''Create a simple scene with a trivial hierarchy:
A translation (1, 2, 3)
|_B translation (7, 8, 9)
Returns a tuple of:
- proxy shape UFE item
- A translation op, A translation vector
- A UFE path string, A UFE path, A UFE item
- B translation op, B translation vector
- B UFE path string, B UFE path, B UFE item
Note: the proxy shape path and path string are not returned for compatibility with existing tests.
'''
(psPathStr, psPath, ps) = createSimpleStage()
(aXlateOp, aXlation, aUsdUfePathStr, aUsdUfePath, aUsdItem,
bXlateOp, bXlation, bUsdUfePathStr, bUsdUfePath, bUsdItem) = createSimpleXformSceneInCurrentLayer(psPathStr, ps)
return (ps,
aXlateOp, aXlation, aUsdUfePathStr, aUsdUfePath, aUsdItem,
bXlateOp, bXlation, bUsdUfePathStr, bUsdUfePath, bUsdItem)

def createLayeredStage(layersCount = 3):
'''Create a stage with multiple layers, by default 3 extra layers:
Returns a tuple of:
- proxy shape UFE path string
- proxy shape UFE path
- proxy shape UFE item
- list of root layer and additional layers, from top to bottom
'''
(psPathStr, psPath, ps) = createSimpleStage()

import os
print(os.environ)
stage = mayaUsd.lib.GetPrim(psPathStr).GetStage()
layer = stage.GetRootLayer()
layers = [layer]
for i in range(layersCount):
newLayerName = 'Layer_%d' % (i+1)
usdFormat = Sdf.FileFormat.FindByExtension('usd')
newLayer = Sdf.Layer.New(usdFormat, newLayerName)
layer.subLayerPaths.append(newLayer.identifier)
layer = newLayer
layers.append(layer)

return (psPathStr, psPath, ps, layers)

0 comments on commit 6f3a92b

Please sign in to comment.