diff --git a/lib/mayaUsd/commands/Readme.md b/lib/mayaUsd/commands/Readme.md index 45bcc42cef..639f2d4751 100644 --- a/lib/mayaUsd/commands/Readme.md +++ b/lib/mayaUsd/commands/Readme.md @@ -156,7 +156,7 @@ their own purposes, similar to the Alembic export chaser example. | `-exportColorSets` | `-cls` | bool | true | Enable or disable the export of color sets | | `-exportMaterials` | `-mat` | bool | true | Enable or disable the export of materials | | `-exportAssignedMaterials` | `-ama` | bool | true | Export materials only if they are assigned to a mesh | -| `-exportMaterialUnderPrim` | `-mup` | bool | false | Export materials under the prim that is using them | +| `-legacyMaterialScope` | `-lms` | bool | false | Export materials under a scope determined using the same algorithm as MayaUSD circa 0.28 | | `-exportInstances` | `-ein` | bool | true | Enable or disable the export of instances | | `-referenceObjectMode` | `-rom` | string | `none` | Determines how to export reference objects for meshes. The reference object's points are exported as a primvar on the mesh object; the primvar name is determined by querying `UsdUtilsGetPrefName()`, which defaults to `pref`. Valid values are: `none` - No reference objects are exported, `attributeOnly` - Only meshes set with a valid "referenceObject" attached will be exported, `defaultToMesh` - Meshes with no "referenceObject" attached will export their own points | | `-exportRefsAsInstanceable` | `-eri` | bool | false | Will cause all references created by USD reference assembly nodes or explicitly tagged reference nodes to be set to be instanceable (`UsdPrim::SetInstanceable(true)`). | diff --git a/lib/mayaUsd/commands/baseExportCommand.cpp b/lib/mayaUsd/commands/baseExportCommand.cpp index 554a63ed7f..82a73519a7 100644 --- a/lib/mayaUsd/commands/baseExportCommand.cpp +++ b/lib/mayaUsd/commands/baseExportCommand.cpp @@ -124,8 +124,8 @@ MSyntax MayaUSDExportCommand::createSyntax() UsdMayaJobExportArgsTokens->exportAssignedMaterials.GetText(), MSyntax::kBoolean); syntax.addFlag( - kExportMaterialUnderPrimFlag, - UsdMayaJobExportArgsTokens->exportMaterialUnderPrim.GetText(), + kLegacyMaterialScopeFlag, + UsdMayaJobExportArgsTokens->legacyMaterialScope.GetText(), MSyntax::kBoolean); syntax.addFlag( kStripNamespacesFlag, diff --git a/lib/mayaUsd/commands/baseExportCommand.h b/lib/mayaUsd/commands/baseExportCommand.h index 1d7c899c86..cd5f9916c1 100644 --- a/lib/mayaUsd/commands/baseExportCommand.h +++ b/lib/mayaUsd/commands/baseExportCommand.h @@ -47,7 +47,7 @@ class MAYAUSD_CORE_PUBLIC MayaUSDExportCommand : public MPxCommand static constexpr auto kExportColorSetsFlag = "cls"; static constexpr auto kExportMaterialsFlag = "mat"; static constexpr auto kExportAssignedMaterialsFlag = "ama"; - static constexpr auto kExportMaterialUnderPrimFlag = "mup"; + static constexpr auto kLegacyMaterialScopeFlag = "lms"; static constexpr auto kExportUVsFlag = "uvs"; static constexpr auto kExportRelativeTexturesFlag = "rtx"; static constexpr auto kEulerFilterFlag = "ef"; diff --git a/lib/mayaUsd/fileio/jobs/jobArgs.cpp b/lib/mayaUsd/fileio/jobs/jobArgs.cpp index ecc029a8fe..c4be3c3ddf 100644 --- a/lib/mayaUsd/fileio/jobs/jobArgs.cpp +++ b/lib/mayaUsd/fileio/jobs/jobArgs.cpp @@ -603,8 +603,7 @@ UsdMayaJobExportArgs::UsdMayaJobExportArgs( , exportMaterials(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportMaterials)) , exportAssignedMaterials( extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportAssignedMaterials)) - , exportMaterialUnderPrim( - extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportMaterialUnderPrim)) + , legacyMaterialScope(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->legacyMaterialScope)) , exportDefaultCameras(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->defaultCameras)) , exportDisplayColor(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportDisplayColor)) , exportDistanceUnit(extractBoolean(userArgs, UsdMayaJobExportArgsTokens->exportDistanceUnit)) @@ -731,8 +730,7 @@ std::ostream& operator<<(std::ostream& out, const UsdMayaJobExportArgs& exportAr << "exportMaterials: " << TfStringify(exportArgs.exportMaterials) << std::endl << "exportAssignedMaterials: " << TfStringify(exportArgs.exportAssignedMaterials) << std::endl - << "exportMaterialUnderPrim: " << TfStringify(exportArgs.exportMaterialUnderPrim) - << std::endl + << "legacyMaterialScope: " << TfStringify(exportArgs.legacyMaterialScope) << std::endl << "exportDefaultCameras: " << TfStringify(exportArgs.exportDefaultCameras) << std::endl << "exportDisplayColor: " << TfStringify(exportArgs.exportDisplayColor) << std::endl << "exportDistanceUnit: " << TfStringify(exportArgs.exportDistanceUnit) << std::endl @@ -1023,7 +1021,7 @@ const VtDictionary& UsdMayaJobExportArgs::GetDefaultDictionary() d[UsdMayaJobExportArgsTokens->exportColorSets] = true; d[UsdMayaJobExportArgsTokens->exportMaterials] = true; d[UsdMayaJobExportArgsTokens->exportAssignedMaterials] = true; - d[UsdMayaJobExportArgsTokens->exportMaterialUnderPrim] = false; + d[UsdMayaJobExportArgsTokens->legacyMaterialScope] = false; d[UsdMayaJobExportArgsTokens->exportDisplayColor] = false; d[UsdMayaJobExportArgsTokens->exportDistanceUnit] = true; d[UsdMayaJobExportArgsTokens->exportInstances] = true; @@ -1128,7 +1126,7 @@ const VtDictionary& UsdMayaJobExportArgs::GetGuideDictionary() d[UsdMayaJobExportArgsTokens->exportColorSets] = _boolean; d[UsdMayaJobExportArgsTokens->exportMaterials] = _boolean; d[UsdMayaJobExportArgsTokens->exportAssignedMaterials] = _boolean; - d[UsdMayaJobExportArgsTokens->exportMaterialUnderPrim] = _boolean; + d[UsdMayaJobExportArgsTokens->legacyMaterialScope] = _boolean; d[UsdMayaJobExportArgsTokens->exportDisplayColor] = _boolean; d[UsdMayaJobExportArgsTokens->exportDistanceUnit] = _boolean; d[UsdMayaJobExportArgsTokens->exportInstances] = _boolean; diff --git a/lib/mayaUsd/fileio/jobs/jobArgs.h b/lib/mayaUsd/fileio/jobs/jobArgs.h index 59473aa260..e44b2e03d7 100644 --- a/lib/mayaUsd/fileio/jobs/jobArgs.h +++ b/lib/mayaUsd/fileio/jobs/jobArgs.h @@ -72,7 +72,7 @@ TF_DECLARE_PUBLIC_TOKENS( (exportColorSets) \ (exportMaterials) \ (exportAssignedMaterials) \ - (exportMaterialUnderPrim) \ + (legacyMaterialScope) \ (exportDisplayColor) \ (exportDistanceUnit) \ (exportInstances) \ @@ -210,7 +210,7 @@ struct UsdMayaJobExportArgs const bool exportColorSets; const bool exportMaterials; const bool exportAssignedMaterials; - const bool exportMaterialUnderPrim; + const bool legacyMaterialScope; const bool exportDefaultCameras; const bool exportDisplayColor; const bool exportDistanceUnit; diff --git a/lib/mayaUsd/fileio/primUpdaterManager.cpp b/lib/mayaUsd/fileio/primUpdaterManager.cpp index 8822fa8bca..427c720d6b 100644 --- a/lib/mayaUsd/fileio/primUpdaterManager.cpp +++ b/lib/mayaUsd/fileio/primUpdaterManager.cpp @@ -593,7 +593,7 @@ PushCustomizeSrc pushExport( // This ensures the materials will be under the prim, so that // when exported it is under the node being merged and will thus // be merged too. - userArgs[UsdMayaJobExportArgsTokens->exportMaterialUnderPrim] = true; + userArgs[UsdMayaJobExportArgsTokens->legacyMaterialScope] = true; UsdMayaJobExportArgs jobArgs = UsdMayaJobExportArgs::CreateFromDictionary( userArgs, dagPaths, fullObjectList, timeSamples); diff --git a/lib/mayaUsd/fileio/shading/shadingModeExporterContext.cpp b/lib/mayaUsd/fileio/shading/shadingModeExporterContext.cpp index 33d882c385..6d126a95ea 100644 --- a/lib/mayaUsd/fileio/shading/shadingModeExporterContext.cpp +++ b/lib/mayaUsd/fileio/shading/shadingModeExporterContext.cpp @@ -384,16 +384,36 @@ static SdfPath _GetCommonAncestor( return commonAncestor; } -static UsdPrim _GetMaterialParent( +static UsdPrim _GetLegacyMaterialParent( const UsdStageRefPtr& stage, - const std::string& defaultPrim, const TfToken& materialsScopeName, const UsdMayaShadingModeExportContext::AssignmentVector& assignments) { SdfPath shaderExportLocation = _GetCommonAncestor(stage, assignments); - if (shaderExportLocation.IsEmpty()) - shaderExportLocation = SdfPath::AbsoluteRootPath(); + if (shaderExportLocation.IsEmpty()) { + return UsdPrim(); + } + + if (shaderExportLocation == SdfPath::AbsoluteRootPath()) { + return stage->GetPseudoRoot(); + } + + while (!shaderExportLocation.IsRootPrimPath()) { + shaderExportLocation = shaderExportLocation.GetParentPath(); + } + + shaderExportLocation = shaderExportLocation.AppendChild(materialsScopeName); + + return UsdGeomScope::Define(stage, shaderExportLocation).GetPrim(); +} + +static UsdPrim _GetMaterialParent( + const UsdStageRefPtr& stage, + const std::string& defaultPrim, + const TfToken& materialsScopeName) +{ + SdfPath shaderExportLocation = SdfPath::AbsoluteRootPath(); if (!defaultPrim.empty() && defaultPrim != materialsScopeName && defaultPrim != "None") shaderExportLocation = shaderExportLocation.AppendChild(TfToken(defaultPrim)); @@ -540,8 +560,6 @@ UsdPrim UsdMayaShadingModeExportContext::MakeStandardMaterialPrim( const AssignmentsInfo& assignmentsInfo, const std::string& name) const { - static const AssignmentVector emptyAssignments; - const UsdMayaJobExportArgs& exportArgs = GetExportArgs(); if (!shouldExportMaterial(assignmentsInfo, GetSurfaceShader(), exportArgs)) @@ -552,12 +570,11 @@ UsdPrim UsdMayaShadingModeExportContext::MakeStandardMaterialPrim( if (materialName.empty()) return UsdPrim(); - const AssignmentVector& assignments - = exportArgs.exportMaterialUnderPrim ? assignmentsInfo.assignments : emptyAssignments; - UsdStageRefPtr stage = GetUsdStage(); - UsdPrim materialParent = _GetMaterialParent( - stage, exportArgs.defaultPrim, exportArgs.materialsScopeName, assignments); + UsdPrim materialParent = exportArgs.legacyMaterialScope + ? _GetLegacyMaterialParent( + stage, exportArgs.materialsScopeName, assignmentsInfo.assignments) + : _GetMaterialParent(stage, exportArgs.defaultPrim, exportArgs.materialsScopeName); if (!materialParent) return UsdPrim(); diff --git a/plugin/adsk/scripts/mayaUsdTranslatorExport.mel b/plugin/adsk/scripts/mayaUsdTranslatorExport.mel index b7f87b0c83..f467c1a3fc 100644 --- a/plugin/adsk/scripts/mayaUsdTranslatorExport.mel +++ b/plugin/adsk/scripts/mayaUsdTranslatorExport.mel @@ -1352,6 +1352,9 @@ global proc int mayaUsdTranslatorExport (string $parent, // Append the final excludeExportTypes options to the options list $currentOptions = $currentOptions + ";" + "excludeExportTypes=[" + $excludeExportTypes + "]"; + // Make sure we use the new material scope logic, not the legacy one. + $currentOptions = $currentOptions + ";legacyMaterialScope=0"; + eval($resultCallback+" \""+$currentOptions+"\""); $bResult = 1; diff --git a/test/lib/usd/translators/CMakeLists.txt b/test/lib/usd/translators/CMakeLists.txt index 03fcc86a49..246d525831 100644 --- a/test/lib/usd/translators/CMakeLists.txt +++ b/test/lib/usd/translators/CMakeLists.txt @@ -250,6 +250,7 @@ endforeach() if (MAYA_APP_VERSION VERSION_GREATER 2022) set(MTLX_TEST_SCRIPT_FILES testUsdExportMaterialScope.py + testUsdExportMaterialScopeLegacy.py testUsdExportMaterialsOnly.py testUsdExportAssignedMaterials.py testUsdExportMaterialX.py diff --git a/test/lib/usd/translators/testUsdExportMaterialScopeLegacy.py b/test/lib/usd/translators/testUsdExportMaterialScopeLegacy.py new file mode 100644 index 0000000000..9a3cb116b7 --- /dev/null +++ b/test/lib/usd/translators/testUsdExportMaterialScopeLegacy.py @@ -0,0 +1,122 @@ +#!/usr/bin/env mayapy +# +# Copyright 2024 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. +# + +from pxr import Usd +from pxr import UsdShade +from pxr import Sdr + +from maya import cmds +from maya import standalone + +import os +import unittest + +import fixturesUtils + + +class testUsdExportMaterialScopeLegacy(unittest.TestCase): + + _oneGroupMayaFileName = 'one-group.ma' + _twoGroupsMayaFileName = 'two-groups.ma' + + @classmethod + def setUpClass(cls): + cls.inputPath = fixturesUtils.setUpClass(__file__) + + @classmethod + def tearDownClass(cls): + standalone.uninitialize() + + def _export(self, mayaFileName, defaultPrim=''): + self._mayaFile = os.path.join(self.inputPath, 'UsdExportMaterialScopeTest', mayaFileName) + cmds.file(self._mayaFile, force=True, open=True) + + # Export to USD. + self._usdFilePath = os.path.abspath('MaterialScopeTest.usda') + cmds.mayaUSDExport( + mergeTransformAndShape=True, + file=self._usdFilePath, + shadingMode='useRegistry', + convertMaterialsTo=['UsdPreviewSurface'], + materialsScopeName='Materials', + legacyMaterialScope=True, + defaultPrim=defaultPrim) + + self._stage = Usd.Stage.Open(self._usdFilePath) + + def _verifyMaterials(self, expectedMtlPaths): + for expectedPath in expectedMtlPaths: + prim = self._stage.GetPrimAtPath(expectedPath) + self.assertTrue(prim, 'Expected to find %s when exporting %s to %s' % (expectedPath, self._mayaFile, self._usdFilePath)) + shader = UsdShade.Shader(prim) + self.assertTrue(shader, 'Expected prim at %s to be a shader when exporting %s to %s' % (expectedPath, self._mayaFile, self._usdFilePath)) + self.assertEqual(shader.GetIdAttr().Get(), 'UsdPreviewSurface') + + def testStageOpens(self): + ''' + Tests that the export can be done and that the USD stage can be opened successfully. + ''' + self._export(self._oneGroupMayaFileName) + self.assertTrue(self._stage) + + def testMaterialScopeOneGroupNoDefaultPrim(self): + self._export(self._oneGroupMayaFileName) + + expectedMtlPaths = [ + '/top_group/Materials/standardSurface2SG/standardSurface2', + '/top_group/Materials/standardSurface3SG/standardSurface3', + '/top_group/Materials/standardSurface4SG/standardSurface4', + '/top_group/Materials/standardSurface5SG/standardSurface5', + ] + self._verifyMaterials(expectedMtlPaths) + + def testMaterialScopeOneGroupWithDefaultPrim(self): + self._export(self._oneGroupMayaFileName, defaultPrim='top_group') + + expectedMtlPaths = [ + '/top_group/Materials/standardSurface2SG/standardSurface2', + '/top_group/Materials/standardSurface3SG/standardSurface3', + '/top_group/Materials/standardSurface4SG/standardSurface4', + '/top_group/Materials/standardSurface5SG/standardSurface5', + ] + self._verifyMaterials(expectedMtlPaths) + + def testMaterialScopeTwoGroupsNoDefaultPrim(self): + self._export(self._twoGroupsMayaFileName) + + expectedMtlPaths = [ + '/group1/Materials/standardSurface2SG/standardSurface2', + '/group1/Materials/standardSurface3SG/standardSurface3', + '/group2/Materials/standardSurface4SG/standardSurface4', + '/pCylinder1/Materials/standardSurface5SG/standardSurface5', + ] + self._verifyMaterials(expectedMtlPaths) + + def testMaterialScopeTwoGroupsWithDefaultPrim(self): + self._export(self._twoGroupsMayaFileName, defaultPrim='group1') + + expectedMtlPaths = [ + '/group1/Materials/standardSurface2SG/standardSurface2', + '/group1/Materials/standardSurface3SG/standardSurface3', + '/group2/Materials/standardSurface4SG/standardSurface4', + '/pCylinder1/Materials/standardSurface5SG/standardSurface5', + ] + self._verifyMaterials(expectedMtlPaths) + + +if __name__ == '__main__': + unittest.main(verbosity=2)