Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EMSUSD-498 support ignoring variants when merging to USD #3324

Merged
merged 1 commit into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/mayaUsd/commands/PullPushCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ namespace {
static constexpr auto kExportOptionsFlag = "exo";
static constexpr auto kExportOptionsFlagLong = "exportOptions";

static constexpr auto kIgnoreVariantsFlag = "iva";
static constexpr auto kIgnoreVariantsFlagLong = "ignoreVariants";

// Reports an error to the Maya scripting console.
void reportError(const MString& errorString) { MGlobal::displayError(errorString); }

Expand Down Expand Up @@ -229,6 +232,7 @@ MSyntax MergeToUsdCommand::createSyntax()
{
MSyntax syntax = createSyntaxWithUfeArgs(1);
syntax.addFlag(kExportOptionsFlag, kExportOptionsFlagLong, MSyntax::kString);
syntax.addFlag(kIgnoreVariantsFlag, kIgnoreVariantsFlagLong, MSyntax::kBoolean);
return syntax;
}

Expand Down Expand Up @@ -275,6 +279,8 @@ MStatus MergeToUsdCommand::doIt(const MArgList& argList)
return status;
}

userArgs[UsdMayaPrimUpdaterArgsTokens->ignoreVariants] = argData.isFlagSet(kIgnoreVariantsFlag);

// Scope the undo item recording so we can undo on failure.
{
OpUndoItemRecorder undoRecorder(fUndoItemList);
Expand Down
1 change: 1 addition & 0 deletions lib/mayaUsd/fileio/primUpdater.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ UsdMayaPrimUpdater::PushCopySpecs UsdMayaPrimUpdater::pushCopySpecs(
const SdfPath& dstSdfPath)
{
MayaUsdUtils::MergePrimsOptions options;
options.ignoreVariants = _context ? _context->GetArgs()._ignoreVariants : false;
return MayaUsdUtils::mergePrims(
srcStage, srcLayer, srcSdfPath, dstStage, dstLayer, dstSdfPath, options)
? PushCopySpecs::Continue
Expand Down
6 changes: 5 additions & 1 deletion lib/mayaUsd/fileio/primUpdaterArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ static bool _Boolean(const VtDictionary& userArgs, const TfToken& key)

UsdMayaPrimUpdaterArgs::UsdMayaPrimUpdaterArgs(const VtDictionary& userArgs)
: _copyOperation(_Boolean(userArgs, UsdMayaPrimUpdaterArgsTokens->copyOperation))
, _ignoreVariants(_Boolean(userArgs, UsdMayaPrimUpdaterArgsTokens->ignoreVariants))
{
}

Expand All @@ -50,7 +51,10 @@ 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;
d[UsdMayaPrimUpdaterArgsTokens->ignoreVariants] = false;
});

return d;
}
Expand Down
8 changes: 5 additions & 3 deletions lib/mayaUsd/fileio/primUpdaterArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@
PXR_NAMESPACE_OPEN_SCOPE

// clang-format off
#define PXRUSDMAYA_UPDATER_ARGS_TOKENS \
/* Dictionary keys */ \
(copyOperation)
#define PXRUSDMAYA_UPDATER_ARGS_TOKENS \
/* Dictionary keys */ \
(copyOperation) \
(ignoreVariants)
// clang-format on

TF_DECLARE_PUBLIC_TOKENS(
Expand All @@ -42,6 +43,7 @@ TF_DECLARE_PUBLIC_TOKENS(
struct UsdMayaPrimUpdaterArgs
{
const bool _copyOperation { false };
const bool _ignoreVariants { false };

MAYAUSD_CORE_PUBLIC
static UsdMayaPrimUpdaterArgs createFromDictionary(const VtDictionary& userArgs);
Expand Down
4 changes: 2 additions & 2 deletions lib/usd/utils/MergePrims.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ bool isDataAtPathsModified(
}

if (src.path.ContainsPropertyElements()) {
const UsdProperty srcProp = srcPrim.GetPropertyAtPath(src.path);
const UsdProperty srcProp = srcPrim.GetPropertyAtPath(src.path.StripAllVariantSelections());

// Note: we only return early for transform property if the local transform has
// not changed. The reason is that when the transform has changed, we *do*
Expand All @@ -414,7 +414,7 @@ bool isDataAtPathsModified(
}
}

const UsdProperty dstProp = dstPrim.GetPropertyAtPath(dst.path);
const UsdProperty dstProp = dstPrim.GetPropertyAtPath(dst.path.StripAllVariantSelections());
if (!srcProp.IsValid() || !dstProp.IsValid()) {
printInvalidField(ctx, src, "prop", srcProp.IsValid(), dstProp.IsValid());
return srcProp.IsValid() != dstProp.IsValid();
Expand Down
224 changes: 223 additions & 1 deletion test/lib/mayaUsd/fileio/testMergeToUsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import mayaUtils
import mayaUsd.ufe

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

from maya import cmds
from maya import standalone
Expand Down Expand Up @@ -586,5 +586,227 @@ def verifySwitch():
switchParentWithVariant("B")
verifySwitch()

def testMergeComponentInVariants(self):
'''
Merge edits on a component that is kept in a separate file.
That component is inside the variants declared in its parent
prim.

Verify that editing it and merging it inside variants, that is
with the merge option ignoreVariants set to false, the edits will
be authored inside the variants.
'''

# Create a stage from a file.
testFile = getTestScene("multiVariants", "with-variants-root.usda")
testDagPath, stage = mayaUtils.createProxyFromFile(testFile)

# Create a new anonymous layer under the root that will receive the edits.
newLayer = Sdf.Layer.CreateAnonymous()
stage.GetRootLayer().subLayerPaths.append(newLayer.identifier)
stage.SetEditTarget(newLayer)

# UFE path to interesting prims in the USD file.
#
# /group (Xform, variant sets: geo, geo_vis, selection: render_high, render, xformOp inside both variants)
# /GEO (Xform, inside both variants)
# /pCube1 (Mesh, inside both variants)

# Root prim
# This prim has variant set "geo" set to "render_high" and "geo_vis" set to "render"
rootSdfPath = "/group"

geoVariantSetName = "geo"
geoVariantSelection = "render_high"

visVariantSetName = "geo_vis"
visRenderVariant = "render"
visPreviewVariant = "preview"

# This prim is in variants will be edited
editedSdfPath = rootSdfPath + "/GEO"

# Verify that the original scene has the correct variants selected.
def verifyVariantSelections():
rootPrim = stage.GetPrimAtPath(rootSdfPath)
self.assertIsNotNone(rootPrim)
self.assertTrue(rootPrim.IsValid())
self.assertTrue(rootPrim.HasVariantSets())
geoVariantSetVariantSet = rootPrim.GetVariantSets().GetVariantSet(geoVariantSetName)
self.assertIsNotNone(geoVariantSetVariantSet)
self.assertEqual(geoVariantSelection, geoVariantSetVariantSet.GetVariantSelection())
visVariantSetVariantSet = rootPrim.GetVariantSets().GetVariantSet(visVariantSetName)
self.assertIsNotNone(visVariantSetVariantSet)
self.assertEqual(visRenderVariant, visVariantSetVariantSet.GetVariantSelection())

verifyVariantSelections()

# Edit as maya and move the GEO.
cmds.mayaUsdEditAsMaya(testDagPath + "," + editedSdfPath)

editedMayaItem = ufe.GlobalSelection.get().front()
editedMayaPath = editedMayaItem.path()
editedMayaPathStr = ufe.PathString.string(editedMayaPath)

(editedMayaPath, editedMayaPathStr, _, editedMayaMatrix) = \
setMayaTranslation(editedMayaItem, om.MVector(-12, -13, 14))

# Merge back to USD.
cmds.mayaUsdMergeToUsd(editedMayaPathStr)

verifyVariantSelections()

def verifyTranslation():
editedPrim = stage.GetPrimAtPath(editedSdfPath)
self.assertTrue(editedPrim.IsValid())

xformOps = UsdGeom.Xformable(editedPrim).GetOrderedXformOps()
self.assertIsNotNone(xformOps)
self.assertEqual(len(xformOps), 1)
usdMatrix = xformOps[0].GetOpTransform(Usd.TimeCode())

usdValues = [v for row in usdMatrix for v in row]
mayaValues = [v for v in editedMayaMatrix]
assertVectorAlmostEqual(self, mayaValues, usdValues)

verifyTranslation()

# Switch root to variant "preview" and verify that the translation
# on the GEO prim is gone.
#
# For the variant switch to work, we must put the edit target on
# the root layer again.

stage.SetEditTarget(stage.GetRootLayer())

def switchRootVisVariant():
rootPrim = stage.GetPrimAtPath(rootSdfPath)
rootVariantSets = rootPrim.GetVariantSets()
rootVariantSets.SetSelection(visVariantSetName, visPreviewVariant)

def verifySwitch():
editedPrim = stage.GetPrimAtPath(editedSdfPath)
self.assertTrue(editedPrim.IsValid())

xformOps = UsdGeom.Xformable(editedPrim).GetOrderedXformOps()
self.assertListEqual(xformOps, [])

switchRootVisVariant()
verifySwitch()

def testMergeComponentOutsideVariants(self):
'''
Merge edits on a component that is kept in a separate file.
That component is *outside* the variants declared in its parent
prim.

Verify that editing it and merging it *outside* variants, that is
with the merge option ignoreVariants set to true, the edits will
be authored *outside* the variants.
'''

# Create a stage from a file.
testFile = getTestScene("multiVariants", "no-variant-root.usda")
testDagPath, stage = mayaUtils.createProxyFromFile(testFile)

# Create a new anonymous layer under the root that will receive the edits.
newLayer = Sdf.Layer.CreateAnonymous()
stage.GetRootLayer().subLayerPaths.append(newLayer.identifier)
stage.SetEditTarget(newLayer)

# UFE path to interesting prims in the USD file.
#
# /group (Xform, variant sets: geo, geo_vis, selection: render_high, render, xformOp inside both variants)
# /GEO (Xform, outside both variants)
# /pCube1 (Mesh, outside both variants)

# Root prim
# This prim has variant set "geo" set to "render_high" and "geo_vis" set to "render"
rootSdfPath = "/group"

geoVariantSetName = "geo"
geoVariantSelection = "render_high"

visVariantSetName = "geo_vis"
visRenderVariant = "render"
visPreviewVariant = "preview"

# This prim is in variants will be edited
editedSdfPath = rootSdfPath + "/GEO"

# Verify that the original scene has the correct variants selected.
def verifyVariantSelections():
rootPrim = stage.GetPrimAtPath(rootSdfPath)
self.assertIsNotNone(rootPrim)
self.assertTrue(rootPrim.IsValid())
self.assertTrue(rootPrim.HasVariantSets())
geoVariantSetVariantSet = rootPrim.GetVariantSets().GetVariantSet(geoVariantSetName)
self.assertIsNotNone(geoVariantSetVariantSet)
self.assertEqual(geoVariantSelection, geoVariantSetVariantSet.GetVariantSelection())
visVariantSetVariantSet = rootPrim.GetVariantSets().GetVariantSet(visVariantSetName)
self.assertIsNotNone(visVariantSetVariantSet)
self.assertEqual(visRenderVariant, visVariantSetVariantSet.GetVariantSelection())

verifyVariantSelections()

# Edit as maya and move the GEO.
cmds.mayaUsdEditAsMaya(testDagPath + "," + editedSdfPath)

editedMayaItem = ufe.GlobalSelection.get().front()
editedMayaPath = editedMayaItem.path()
editedMayaPathStr = ufe.PathString.string(editedMayaPath)

(editedMayaPath, editedMayaPathStr, _, editedMayaMatrix) = \
setMayaTranslation(editedMayaItem, om.MVector(-12, -13, 14))

# Merge back to USD.
cmds.mayaUsdMergeToUsd(editedMayaPathStr, ignoreVariants=1)

verifyVariantSelections()

def verifyTranslation():
editedPrim = stage.GetPrimAtPath(editedSdfPath)
self.assertTrue(editedPrim.IsValid())

xformOps = UsdGeom.Xformable(editedPrim).GetOrderedXformOps()
self.assertIsNotNone(xformOps)
self.assertEqual(len(xformOps), 1)
usdMatrix = xformOps[0].GetOpTransform(Usd.TimeCode())

usdValues = [v for row in usdMatrix for v in row]
mayaValues = [v for v in editedMayaMatrix]
assertVectorAlmostEqual(self, mayaValues, usdValues)

verifyTranslation()

# Switch root to variant "preview" and verify that the translation
# on the GEO is still there.
#
# For the variant switch to work, we must put the edit target on
# the root layer again.

stage.SetEditTarget(stage.GetRootLayer())

def switchRootVisVariant():
rootPrim = stage.GetPrimAtPath(rootSdfPath)
rootVariantSets = rootPrim.GetVariantSets()
rootVariantSets.SetSelection(visVariantSetName, visPreviewVariant)

def verifySwitch():
editedPrim = stage.GetPrimAtPath(editedSdfPath)
self.assertTrue(editedPrim.IsValid())

xformOps = UsdGeom.Xformable(editedPrim).GetOrderedXformOps()
self.assertIsNotNone(xformOps)
self.assertEqual(len(xformOps), 1)
usdMatrix = xformOps[0].GetOpTransform(Usd.TimeCode())

usdValues = [v for row in usdMatrix for v in row]
mayaValues = [v for v in editedMayaMatrix]
assertVectorAlmostEqual(self, mayaValues, usdValues)

switchRootVisVariant()
verifySwitch()

if __name__ == '__main__':
unittest.main(verbosity=2)
23 changes: 23 additions & 0 deletions test/testSamples/multiVariants/no-variant-preview-component.usda
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#usda 1.0

over Xform "group" (
kind = "component"
)
{
double3 xformOp:translate = (5.8646097315075165, 0, 2.156596612263078)
uniform token[] xformOpOrder = ["xformOp:translate"]

over Xform "GEO"
{
def Mesh "pCube1"
{
float3[] extent = [(-0.5, -0.5, -0.5), (0.5, 0.5, 0.5)]
int[] faceVertexCounts = [4, 4, 4, 4, 4, 4]
int[] faceVertexIndices = [0, 1, 3, 2, 2, 3, 5, 4, 4, 5, 7, 6, 6, 7, 1, 0, 1, 7, 5, 3, 6, 0, 2, 4]
point3f[] points = [(-0.5, -0.5, 0.5), (0.5, -0.5, 0.5), (-0.5, 0.5, 0.5), (0.5, 0.5, 0.5), (-0.5, 0.5, -0.5), (0.5, 0.5, -0.5), (-0.5, -0.5, -0.5), (0.5, -0.5, -0.5)]
color3f[] primvars:displayColor = [(0.4, 0.4, 0.4)]
uniform token subdivisionScheme = "none"
}
}
}

23 changes: 23 additions & 0 deletions test/testSamples/multiVariants/no-variant-render-component.usda
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#usda 1.0

over Xform "group" (
kind = "component"
)
{
double3 xformOp:translate = (5.8646097315075165, 0, 2.156596612263078)
uniform token[] xformOpOrder = ["xformOp:translate"]

over Xform "GEO"
{
def Mesh "pCube1"
{
float3[] extent = [(-0.5, -0.5, -0.5), (0.5, 0.5, 0.5)]
int[] faceVertexCounts = [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
int[] faceVertexIndices = [0, 50, 74, 53, 50, 1, 51, 74, 74, 51, 4, 52, 53, 74, 52, 3, 1, 54, 75, 51, 54, 2, 55, 75, 75, 55, 5, 56, 51, 75, 56, 4, 3, 52, 76, 59, 52, 4, 57, 76, 76, 57, 7, 58, 59, 76, 58, 6, 4, 56, 77, 57, 56, 5, 60, 77, 77, 60, 8, 61, 57, 77, 61, 7, 6, 58, 78, 64, 58, 7, 62, 78, 78, 62, 10, 63, 64, 78, 63, 9, 7, 61, 79, 62, 61, 8, 65, 79, 79, 65, 11, 66, 62, 79, 66, 10, 9, 63, 80, 69, 63, 10, 67, 80, 80, 67, 13, 68, 69, 80, 68, 12, 10, 66, 81, 67, 66, 11, 70, 81, 81, 70, 14, 71, 67, 81, 71, 13, 12, 68, 82, 26, 68, 13, 72, 82, 82, 72, 16, 73, 26, 82, 73, 15, 13, 71, 83, 72, 71, 14, 27, 83, 83, 27, 17, 28, 72, 83, 28, 16, 15, 73, 84, 31, 73, 16, 29, 84, 84, 29, 19, 30, 31, 84, 30, 18, 16, 28, 85, 29, 28, 17, 32, 85, 85, 32, 20, 33, 29, 85, 33, 19, 18, 30, 86, 36, 30, 19, 34, 86, 86, 34, 22, 35, 36, 86, 35, 21, 19, 33, 87, 34, 33, 20, 37, 87, 87, 37, 23, 38, 34, 87, 38, 22, 21, 35, 88, 40, 35, 22, 39, 88, 88, 39, 1, 50, 40, 88, 50, 0, 22, 38, 89, 39, 38, 23, 41, 89, 89, 41, 2, 54, 39, 89, 54, 1, 23, 37, 90, 43, 37, 20, 32, 90, 90, 32, 17, 42, 43, 90, 42, 24, 2, 41, 91, 55, 41, 23, 43, 91, 91, 43, 24, 44, 55, 91, 44, 5, 24, 42, 92, 45, 42, 17, 27, 92, 92, 27, 14, 70, 45, 92, 70, 11, 5, 44, 93, 60, 44, 24, 45, 93, 93, 45, 11, 65, 60, 93, 65, 8, 18, 36, 94, 31, 36, 21, 46, 94, 94, 46, 25, 47, 31, 94, 47, 15, 21, 40, 95, 46, 40, 0, 53, 95, 95, 53, 3, 48, 46, 95, 48, 25, 15, 47, 96, 26, 47, 25, 49, 96, 96, 49, 9, 69, 26, 96, 69, 12, 25, 48, 97, 49, 48, 3, 59, 97, 97, 59, 6, 64, 49, 97, 64, 9]
point3f[] points = [(-0.38888893, -0.38888893, 0.38888893), (0, -0.4375, 0.4375), (0.38888893, -0.38888893, 0.38888893), (-0.4375, 0, 0.4375), (0, 0, 0.5), (0.4375, 0, 0.4375), (-0.38888893, 0.38888893, 0.3888889), (0, 0.4375, 0.4375), (0.38888893, 0.38888893, 0.38888893), (-0.4375, 0.4375, 0), (0, 0.5, 0), (0.4375, 0.4375, 0), (-0.38888893, 0.3888889, -0.38888893), (0, 0.4375, -0.4375), (0.38888893, 0.38888893, -0.38888893), (-0.4375, 0, -0.4375), (0, 0, -0.5), (0.4375, 0, -0.4375), (-0.38888893, -0.38888893, -0.3888889), (0, -0.4375, -0.4375), (0.38888893, -0.38888893, -0.38888893), (-0.4375, -0.4375, 0), (0, -0.5, 0), (0.4375, -0.4375, 0), (0.5, 0, 0), (-0.5, 0, 0), (-0.4375, 0.25, -0.4375), (0.4375, 0.25, -0.4375), (0.25, 0, -0.5), (0, -0.25, -0.5), (-0.25, -0.4375, -0.4375), (-0.4375, -0.25, -0.4375), (0.4375, -0.25, -0.4375), (0.25, -0.4375, -0.4375), (0, -0.5, -0.25), (-0.25, -0.5, 0), (-0.4375, -0.4375, -0.25), (0.4375, -0.4375, -0.25), (0.25, -0.5, 0), (0, -0.5, 0.25), (-0.4375, -0.4375, 0.25), (0.4375, -0.4375, 0.25), (0.5, 0, -0.25), (0.5, -0.25, 0), (0.5, 0, 0.25), (0.5, 0.25, 0), (-0.5, -0.25, 0), (-0.5, 0, -0.25), (-0.5, 0, 0.25), (-0.5, 0.25, 0), (-0.25, -0.4375, 0.4375), (0, -0.25, 0.5), (-0.25, 0, 0.5), (-0.4375, -0.25, 0.4375), (0.25, -0.4375, 0.4375), (0.4375, -0.25, 0.4375), (0.25, 0, 0.5), (0, 0.25, 0.5), (-0.25, 0.4375, 0.4375), (-0.4375, 0.25, 0.4375), (0.4375, 0.25, 0.4375), (0.25, 0.4375, 0.4375), (0, 0.5, 0.25), (-0.25, 0.5, 0), (-0.4375, 0.4375, 0.25), (0.4375, 0.4375, 0.25), (0.25, 0.5, 0), (0, 0.5, -0.25), (-0.25, 0.4375, -0.4375), (-0.4375, 0.4375, -0.25), (0.4375, 0.4375, -0.25), (0.25, 0.4375, -0.4375), (0, 0.25, -0.5), (-0.25, 0, -0.5), (-0.25, -0.25, 0.5), (0.25, -0.25, 0.5), (-0.25, 0.25, 0.5), (0.25, 0.25, 0.5), (-0.25, 0.5, 0.25), (0.25, 0.5, 0.25), (-0.25, 0.5, -0.25), (0.25, 0.5, -0.25), (-0.25, 0.25, -0.5), (0.25, 0.25, -0.5), (-0.25, -0.25, -0.5), (0.25, -0.25, -0.5), (-0.25, -0.5, -0.25), (0.25, -0.5, -0.25), (-0.25, -0.5, 0.25), (0.25, -0.5, 0.25), (0.5, -0.25, -0.25), (0.5, -0.25, 0.25), (0.5, 0.25, -0.25), (0.5, 0.25, 0.25), (-0.5, -0.25, -0.25), (-0.5, -0.25, 0.25), (-0.5, 0.25, -0.25), (-0.5, 0.25, 0.25)]
color3f[] primvars:displayColor = [(0.4, 0.4, 0.4)]
uniform token subdivisionScheme = "none"
}
}
}

Loading