Skip to content

Commit

Permalink
EMSUSD-943 handle up-axis and units when staging a USD file
Browse files Browse the repository at this point in the history
Up-axis and units UI when staging a USD file:
- Add up-axis, unit and conversion method UI to the staging UI.
- Add that the conversion method menu is disabled in the import UI.
- Make import sections collapsable.
- Fix typo in a header file.

Up-axis and units conversion for stages:
- Add Python helpers to do the conversion since MEL cannot manipulate USD.
- Call the helpers from the MEL script that creates the stage.
- Change prefs or modify stage shape based on necessary conversion.

Add unit tests
- Move helper functions to verify transforms to their own file.
- Update some existing tests to use the new functions.
- Add unit tests when stage USD file for up-axis and units conversion.
  • Loading branch information
pierrebai-adsk committed Oct 4, 2024
1 parent 0668bf5 commit fcc05dc
Show file tree
Hide file tree
Showing 19 changed files with 719 additions and 155 deletions.
1 change: 1 addition & 0 deletions lib/mayaUsd/resources/scripts/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ list(APPEND scripts_src
mayaUsdMergeToUSDOptions.py
mayaUsdMergeToUsd.py
mayaUsdOptions.py
mayaUsdStageConversion.py
mayaUsdUtils.py
mayaUsdMayaReferenceUtils.py
)
Expand Down
5 changes: 5 additions & 0 deletions lib/mayaUsd/resources/scripts/mayaUsdLibRegisterStrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ def mayaUsdLibRegisterStrings():
register('kMenuCacheToUsd', 'Cache to USD...')
register('kMenuMergeMayaEdits', 'Merge Maya Edits to USD');

# mayaUsdStageConversion.py
register("kStageConversionUnknownMethod", "Unknown stage conversion method: %s")
register("kStageConversionSuccessful", "Mismatching axis/unit have been converted for accurate scale.")


def registerPluginResource(pluginId, stringId, resourceStr):
'''See registerPluginResource.mel in Maya.
Expand Down
135 changes: 135 additions & 0 deletions lib/mayaUsd/resources/scripts/mayaUsdStageConversion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import maya.cmds as _cmds
import maya.api.OpenMaya as _om
import mayaUsd.ufe as _ufe

from mayaUsdLibRegisterStrings import getMayaUsdLibString as _getMayaUsdLibString

from pxr import UsdGeom as _UsdGeom


def convertUpAxisAndUnit(shapeNode, convertUpAxis, convertUnit, conversionMethod):
'''
Edit the USD stage associated with the given Maya stage proxy node to
convert the up-axis or the units used to match what is in the USD file
with what Maya is using.
'''
# If neither up-axis nor unit are requested to be modified, return immediately.
conversionInfo = StageConversionInfo(shapeNode, convertUpAxis, convertUnit)
if not conversionInfo.needUnitsConversion and not conversionInfo.needUpAxisConversion:
return

resultMsg = _getMayaUsdLibString("kStageConversionSuccessful")

if conversionMethod.lower() == 'rotatescale':
convertUpAxisAndUnitByModifyingStage(conversionInfo)
elif conversionMethod.lower() == 'overwriteprefs':
convertUpAxisAndUnitByModifyingPrefs(conversionInfo)
else:
resultMsg = _getMayaUsdLibString("kStageConversionUnknownMethod") % conversionMethod

print(resultMsg)


class StageConversionInfo:
'''
Analyze the contents of the USD file and compare it to the Maya settings
to determine what actions need to be done to match them.
'''

@staticmethod
def _isMayaUpAxisZ():
return _cmds.upAxis(query=True, axis=True).lower() == 'z'

@staticmethod
def _isUsdUpAxisZ(stage):
return _UsdGeom.GetStageUpAxis(stage).lower() == 'z'

_mayaToMetersPerUnit = {
_om.MDistance.kInches : _UsdGeom.LinearUnits.inches,
_om.MDistance.kFeet : _UsdGeom.LinearUnits.feet,
_om.MDistance.kYards : _UsdGeom.LinearUnits.yards,
_om.MDistance.kMiles : _UsdGeom.LinearUnits.miles,
_om.MDistance.kMillimeters : _UsdGeom.LinearUnits.millimeters,
_om.MDistance.kCentimeters : _UsdGeom.LinearUnits.centimeters,
_om.MDistance.kKilometers : _UsdGeom.LinearUnits.kilometers,
_om.MDistance.kMeters : _UsdGeom.LinearUnits.meters,
}

@staticmethod
def _convertMayaUnitToMetersPerUnit(mayaUnits):
if mayaUnits not in StageConversionInfo._mayaToMetersPerUnit:
return _UsdGeom.LinearUnits.centimeters
return StageConversionInfo._mayaToMetersPerUnit[mayaUnits]

_metersPerUnitToMayaUnitName = {
_UsdGeom.LinearUnits.inches : "inch",
_UsdGeom.LinearUnits.feet : "foot",
_UsdGeom.LinearUnits.yards : "yard",
_UsdGeom.LinearUnits.miles : "mile",
_UsdGeom.LinearUnits.millimeters : "mm",
_UsdGeom.LinearUnits.centimeters : "cn",
_UsdGeom.LinearUnits.kilometers : "km",
_UsdGeom.LinearUnits.meters : "m",
}

@staticmethod
def _convertMetersPerUnitToMayaUnitName(metersPerUnit):
if metersPerUnit not in StageConversionInfo._metersPerUnitToMayaUnitName:
return "cm"
return StageConversionInfo._metersPerUnitToMayaUnitName[metersPerUnit]

@staticmethod
def _getMayaMetersPerUnit():
mayaUnits = _om.MDistance.internalUnit()
return StageConversionInfo._convertMayaUnitToMetersPerUnit(mayaUnits)

@staticmethod
def _getUsdMetersPerUnit(stage):
return _UsdGeom.GetStageMetersPerUnit(stage)

@staticmethod
def _getStageFromShapeNode(shapeNode):
res = _cmds.ls(shapeNode, l=True)
fullStageName = res[0]
return _ufe.getStage(fullStageName)

def __init__(self, shapeNode, convertUpAxis, convertUnit):
self.shapeNode = shapeNode
self.stage = self._getStageFromShapeNode(shapeNode)

self.isMayaUpAxisZ = self._isMayaUpAxisZ()
self.isUsdUpAxisZ = self._isUsdUpAxisZ(self.stage)
self.needUpAxisConversion = convertUpAxis and (self.isMayaUpAxisZ != self.isUsdUpAxisZ)

self.mayaMetersPerUnit = self._getMayaMetersPerUnit()
self.usdMetersPerUnit = self._getUsdMetersPerUnit(self.stage)
self.needUnitsConversion = convertUnit and (self.mayaMetersPerUnit != self.usdMetersPerUnit)


def convertUpAxisAndUnitByModifyingStage(conversionInfo):
'''
Handle the differences of up-axis and units from the USD file by modifying
the Maya proxy shape node transform to compensate for the differences.
'''
if conversionInfo.needUpAxisConversion:
angle = 90 if conversionInfo.isMayaUpAxisZ else -90
_cmds.rotate(angle, 0, 0, conversionInfo.shapeNode, relative=True, euler=True, pivot=(0, 0, 0), forceOrderXYZ=True)

if conversionInfo.needUnitsConversion:
factor = conversionInfo.usdMetersPerUnit / conversionInfo.mayaMetersPerUnit
_cmds.scale(factor, factor, factor, conversionInfo.shapeNode, relative=True, pivot=(0, 0, 0), scaleXYZ=True)


def convertUpAxisAndUnitByModifyingPrefs(conversionInfo):
'''
Handle the differences of up-axis and units from the USD file by modifying
the Maya preferences to match the USD file.
'''
if conversionInfo.needUpAxisConversion:
newAxis = 'z' if conversionInfo.isUsdUpAxisZ else 'y'
_cmds.upAxis(axis=newAxis)

if conversionInfo.needUnitsConversion:
newUnit = conversionInfo._convertMetersPerUnitToMayaUnitName(conversionInfo.usdMetersPerUnit)
_cmds.currentUnit(linear=newUnit)

2 changes: 1 addition & 1 deletion lib/mayaUsd/utils/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ double ConvertMDistanceUnitToUsdGeomLinearUnit(const MDistance::Unit mdistanceUn
MAYAUSD_CORE_PUBLIC
MDistance::Unit ConvertUsdGeomLinearUnitToMDistanceUnit(const double linearUnit);

/// Convert the given \p mdistanceYnit into its text representation suitable
/// Convert the given \p mdistanceUnit into its text representation suitable
/// to be used with the currentUnit MEL command. Invalid units return "cm".
MAYAUSD_CORE_PUBLIC
MString ConvertMDistanceUnitToText(const MDistance::Unit mdistanceUnit);
Expand Down
18 changes: 18 additions & 0 deletions plugin/adsk/scripts/mayaUSDRegisterStrings.mel
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,24 @@ global proc mayaUSDRegisterStrings()
register("kLabelStage", "Stage");
register("kLabelStageSource", "Stage Source");
register("kLabelStageDisplay", "Stage Display");
register("kStageAdvancedLabel", "Advanced");
register("kStageAxisUnitConversionLabel", "Axis & Unit Conversion");
register("kStageUpAxisLabel", "Up Axis");
register("kStageUpAxisAnn", "If selected, when an up axis mismatch is detected\n" +
"between the imported data and your scene preferences,\n" +
"an automatic correction will be performed.");
register("kStageUnitLabel", "Unit");
register("kStageUnitAnn", "If selected, when a unit mismatch is detected\n" +
"between the imported data and your scene preferences,\n" +
"an automatic correction will be performed.");
register("kStageAxisAndUnitMethod", "Method");
// Note: initial <b> is used to force Qt to render the text as HTML.
register("kStageAxisAndUnitMethodAnn", "<b></b>Select the method for axis/unit conversions.<br/>" +
"<br/>" +
"<b>Rotate/Scale</b>: Rotate/Scale the stage.<br/>" +
"<b>Overwrite Maya Preferences</b>: Update Maya's axis/unit preferences based on the imported data.");
register("kStageAxisAndUnitRotateScale", "Rotate/Scale");
register("kStageAxisAndUnitOverwritePrefs", "Overwrite Maya Preferences");
register("kLoad", "Load");
register("kLoadPayloads", "Load Payloads:");
register("kLoadPayloadsAnn", "When on, loads all prims marked as payloads. When off, all prims marked as payloads and their children are not loaded.");
Expand Down
3 changes: 1 addition & 2 deletions plugin/adsk/scripts/mayaUsdTranslatorExport.mel
Original file line number Diff line number Diff line change
Expand Up @@ -1333,8 +1333,7 @@ global proc int mayaUsdTranslatorExport (string $parent,
}

if ($canControlUpAxisAndUnit) {
int $collapse = stringArrayContains("axisAndUnit", $expandedSections) ? false : true;
frameLayout -label `getMayaUsdString("kExportAxisAndUnitLbl")` -collapsable true -collapse $collapse axisAndUnitFrameLayout;
frameLayout -label `getMayaUsdString("kExportAxisAndUnitLbl")` -collapsable true -collapse false axisAndUnitFrameLayout;
separator -style "none";

optionMenuGrp -l `getMayaUsdString("kExportUpAxisLbl")` -annotation `getMayaUsdString("kExportUpAxisAnn")` upAxisPopup;
Expand Down
31 changes: 25 additions & 6 deletions plugin/adsk/scripts/mayaUsdTranslatorImport.mel
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,10 @@ global proc mayaUsdTranslatorImport_SetFromOptions(string $currentOptions, int $
intFieldGrp -e -value2 $endTime -en2 $enable mayaUsdTranslator_CustomFrameRange;
} else if ($optionBreakDown[0] == "upAxis") {
mayaUsdTranslatorImport_SetCheckBoxGrp($optionBreakDown[1], $enable, "mayaUsdTranslator_ImportUpAxisCheckBox");
mayaUsdTranslatorImport_upAxisUnitCB();
} else if ($optionBreakDown[0] == "unit") {
mayaUsdTranslatorImport_SetCheckBoxGrp($optionBreakDown[1], $enable, "mayaUsdTranslator_ImportUnitCheckBox");
mayaUsdTranslatorImport_upAxisUnitCB();
} else if ($optionBreakDown[0] == "axisAndUnitMethod") {
mayaUsdTranslatorImport_SetOptionMenuByAnnotation($optionBreakDown[1], $enable, "mayaUsdTranslator_ImportkImportAxisAndUnitMethodMenu");
} else if ($optionBreakDown[0] == "useCustomFrameRange") {
Expand Down Expand Up @@ -519,7 +521,7 @@ global proc int mayaUsdTranslatorImport (string $parent,
// menuItem -label "Object space";
// menuItem -label "World space";
// optionMenuGrp -e -sl 1 mayaUsdTranslator_CoordSystemOptionMenu;
frameLayout -label `getMayaUsdString("kImportMaterialsLbl")` materialsFrameLayout;
frameLayout -label `getMayaUsdString("kImportMaterialsLbl")` -collapsable true -collapse false materialsFrameLayout;
separator -style "none";
checkBoxGrp -label "" -label1 `getMayaUsdString("kImportMaterialsLbl")` -cw 1 $cw1 -value1 1 -ann `getMayaUsdString("kImportMaterialsAnn")` -cc ("mayaUsdTranslatorImport_MaterialsCB") mayaUsdTranslator_MaterialsCheckBox;

Expand Down Expand Up @@ -565,7 +567,7 @@ global proc int mayaUsdTranslatorImport (string $parent,
// menuItem "Custom";
// optionMenuGrp -e -sl 2 mayaUsdTranslator_IncludeCustomAttribOptionMenu;

frameLayout -label "Animation" animationFrameLayout;
frameLayout -label "Animation" -collapsable true -collapse false animationFrameLayout;
separator -style "none";
checkBoxGrp -label "" -label1 `getMayaUsdString("kImportAnimationDataLbl")` -cw 1 $cw1 -cc ("mayaUsdTranslatorImport_AnimationCB") mayaUsdTranslator_AnimDataCheckBox;

Expand All @@ -580,18 +582,26 @@ global proc int mayaUsdTranslatorImport (string $parent,
separator -style "none";
setParent ..;

frameLayout -label "Advanced" advancedFrameLayout;
frameLayout -label "Advanced" -collapsable true -collapse true advancedFrameLayout;
separator -style "none";
checkBoxGrp -label "" -label1 `getMayaUsdString("kImportToInstanceOpt")` -cw 1 $cw1 -value1 1 -ann `getMayaUsdString("kImportToInstanceAnn")` mayaUsdTranslator_ImportInstancesCheckBox;
separator -style "none";

frameLayout -label `getMayaUsdString("kImportAxisAndUnit")` axisAndUnitFrameLayout;
checkBoxGrp -label "" -label1 `getMayaUsdString("kImportUpAxis")` -cw 1 $cw1 -value1 1 -ann `getMayaUsdString("kImportUpAxisAnn")` mayaUsdTranslator_ImportUpAxisCheckBox;
checkBoxGrp -label "" -label1 `getMayaUsdString("kImportUnit")` -cw 1 $cw1 -value1 1 -ann `getMayaUsdString("kImportUnitAnn")` mayaUsdTranslator_ImportUnitCheckBox;
frameLayout -label `getMayaUsdString("kImportAxisAndUnit")` -collapsable true -collapse false axisAndUnitFrameLayout;
separator -style "none";
checkBoxGrp -label "" -label1 `getMayaUsdString("kImportUpAxis")` -ann `getMayaUsdString("kImportUpAxisAnn")`
-cw 1 $cw1 -value1 1
-cc ("mayaUsdTranslatorImport_upAxisUnitCB")
mayaUsdTranslator_ImportUpAxisCheckBox;
checkBoxGrp -label "" -label1 `getMayaUsdString("kImportUnit")` -ann `getMayaUsdString("kImportUnitAnn")`
-cw 1 $cw1 -value1 1
-cc ("mayaUsdTranslatorImport_upAxisUnitCB")
mayaUsdTranslator_ImportUnitCheckBox;
optionMenuGrp -l `getMayaUsdString("kImportAxisAndUnitMethod")` -cw 1 $cw1 -ann `getMayaUsdString("kImportAxisAndUnitMethodAnn")` mayaUsdTranslator_ImportkImportAxisAndUnitMethodMenu;
menuItem -l `getMayaUsdString("kImportAxisAndUnitRotateScale")` -ann "rotateScale";
menuItem -l `getMayaUsdString("kImportAxisAndUnitAddTransform")` -ann "addTransform";
menuItem -l `getMayaUsdString("kImportAxisAndUnitOverwritePrefs")` -ann "overwritePrefs";
separator -style "none";
setParent ..;
setParent ..;

Expand Down Expand Up @@ -677,6 +687,7 @@ global proc int mayaUsdTranslatorImport (string $parent,
}

mayaUsdTranslatorImport_AnimationCB();
mayaUsdTranslatorImport_upAxisUnitCB();
return $bResult;
}

Expand Down Expand Up @@ -717,6 +728,14 @@ global proc mayaUsdTranslatorImport_AnimationCB()
}
}

// Call when the up-axis or unit checkbox are modified bythe user.
global proc mayaUsdTranslatorImport_upAxisUnitCB()
{
int $upAxisEnabled = `checkBoxGrp -q -value1 mayaUsdTranslator_ImportUpAxisCheckBox`;
int $unitEnabled = `checkBoxGrp -q -value1 mayaUsdTranslator_ImportUnitCheckBox`;
optionMenuGrp -e -en ($unitEnabled + $upAxisEnabled) mayaUsdTranslator_ImportkImportAxisAndUnitMethodMenu;
}

global proc mayaUsdTranslatorImport_MaterialsCB()
{
if (`checkBoxGrp -q -value1 mayaUsdTranslator_MaterialsCheckBox` == 1) {
Expand Down
Loading

0 comments on commit fcc05dc

Please sign in to comment.