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-949 up axis in USD export #3929

Closed
wants to merge 1 commit into from
Closed
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
1 change: 1 addition & 0 deletions lib/mayaUsd/commands/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ their own purposes, similar to the Alembic export chaser example.
| `-verbose` | `-v` | noarg | false | Make the command output more verbose |
| `-customLayerData` | `-cld` | string[3](multi) | none | Set the layers customLayerData metadata. Values are a list of three strings for key, value and data type |
| `-metersPerUnit` | `-mpu` | double | 0.0 | (Evolving) Exports with the given metersPerUnit. Use with care, as only certain attributes have their dimensions converted.<br/><br/> The default value of 0 will continue to use the Maya internal units (cm) and a value of -1 will use the display units. Any other positive value will be taken as an explicit metersPerUnit value to be used.<br/><br/> Currently, the following prim types are supported: <br/><ul><li>Meshes</li><li>Transforms</li></ul> |
| `-upAxis` | `-upa` | string | mayaPrefs | How the up-axis of the exported USD is controlled. "mayaPrefs" follows the current Maya Preferences. "none" does not author up-axis. "y" or "z" author that axis and convert data if teh Maya preferences does not match. |

#### Frame Samples

Expand Down
1 change: 1 addition & 0 deletions lib/mayaUsd/commands/baseExportCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ MSyntax MayaUSDExportCommand::createSyntax()
syntax.addFlag(kRootPrimFlag, UsdMayaJobExportArgsTokens->rootPrim.GetText(), MSyntax::kString);
syntax.addFlag(
kRootPrimTypeFlag, UsdMayaJobExportArgsTokens->rootPrimType.GetText(), MSyntax::kString);
syntax.addFlag(kUpAxisFlag, UsdMayaJobExportArgsTokens->upAxis.GetText(), MSyntax::kString);
syntax.addFlag(
kRenderableOnlyFlag, UsdMayaJobExportArgsTokens->renderableOnly.GetText(), MSyntax::kNoArg);
syntax.addFlag(
Expand Down
1 change: 1 addition & 0 deletions lib/mayaUsd/commands/baseExportCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class MAYAUSD_CORE_PUBLIC MayaUSDExportCommand : public MPxCommand
static constexpr auto kParentScopeFlag = "psc"; // deprecated
static constexpr auto kRootPrimFlag = "rpm";
static constexpr auto kRootPrimTypeFlag = "rpt";
static constexpr auto kUpAxisFlag = "upa";
static constexpr auto kRenderableOnlyFlag = "ro";
static constexpr auto kDefaultCamerasFlag = "dc";
static constexpr auto kRenderLayerModeFlag = "rlm";
Expand Down
9 changes: 9 additions & 0 deletions lib/mayaUsd/fileio/jobs/jobArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,13 @@ UsdMayaJobExportArgs::UsdMayaJobExportArgs(
UsdMayaJobExportArgsTokens->rootPrimType,
UsdMayaJobExportArgsTokens->scope,
{ UsdMayaJobExportArgsTokens->xform }))
, upAxis(extractToken(
userArgs,
UsdMayaJobExportArgsTokens->upAxis,
UsdMayaJobExportArgsTokens->mayaPrefs,
{ UsdMayaJobExportArgsTokens->none,
UsdMayaJobExportArgsTokens->y,
UsdMayaJobExportArgsTokens->z }))
, renderLayerMode(extractToken(
userArgs,
UsdMayaJobExportArgsTokens->renderLayerMode,
Expand Down Expand Up @@ -1137,6 +1144,7 @@ const VtDictionary& UsdMayaJobExportArgs::GetDefaultDictionary()
d[UsdMayaJobExportArgsTokens->parentScope] = std::string(); // Deprecated
d[UsdMayaJobExportArgsTokens->rootPrim] = std::string();
d[UsdMayaJobExportArgsTokens->rootPrimType] = UsdMayaJobExportArgsTokens->scope.GetString();
d[UsdMayaJobExportArgsTokens->upAxis] = UsdMayaJobExportArgsTokens->mayaPrefs.GetString();
d[UsdMayaJobExportArgsTokens->pythonPerFrameCallback] = std::string();
d[UsdMayaJobExportArgsTokens->pythonPostCallback] = std::string();
d[UsdMayaJobExportArgsTokens->renderableOnly] = false;
Expand Down Expand Up @@ -1241,6 +1249,7 @@ const VtDictionary& UsdMayaJobExportArgs::GetGuideDictionary()
d[UsdMayaJobExportArgsTokens->parentScope] = _string; // Deprecated
d[UsdMayaJobExportArgsTokens->rootPrim] = _string;
d[UsdMayaJobExportArgsTokens->rootPrimType] = _string;
d[UsdMayaJobExportArgsTokens->upAxis] = _string;
d[UsdMayaJobExportArgsTokens->pythonPerFrameCallback] = _string;
d[UsdMayaJobExportArgsTokens->pythonPostCallback] = _string;
d[UsdMayaJobExportArgsTokens->renderableOnly] = _boolean;
Expand Down
6 changes: 6 additions & 0 deletions lib/mayaUsd/fileio/jobs/jobArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ TF_DECLARE_PUBLIC_TOKENS(
(parentScope) \
(rootPrim) \
(rootPrimType) \
(upAxis) \
(pythonPerFrameCallback) \
(pythonPostCallback) \
(renderableOnly) \
Expand All @@ -125,6 +126,10 @@ TF_DECLARE_PUBLIC_TOKENS(
(excludeExportTypes) \
/* Special "none" token */ \
(none) \
/* up axis values */ \
(mayaPrefs) \
(y) \
(z) \
/* relative textures values */ \
(automatic) \
(absolute) \
Expand Down Expand Up @@ -258,6 +263,7 @@ struct UsdMayaJobExportArgs
const SdfPath parentScope; // Deprecated, use rootPrim instead.
const SdfPath rootPrim;
const TfToken rootPrimType;
const TfToken upAxis;
const TfToken renderLayerMode;
const TfToken rootKind;
const bool disableModelKindProcessor;
Expand Down
124 changes: 114 additions & 10 deletions lib/mayaUsd/fileio/jobs/writeJob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,112 @@ static TfToken _GetFallbackExtension(const TfToken& compatibilityMode)
return UsdMayaTranslatorTokens->UsdFileExtensionDefault;
}

class AutoUpAxisChanger
{
public:
AutoUpAxisChanger(const UsdStageRefPtr& stage, const TfToken& upAxisOption)
: _stage(stage)
{
_PrepareUpAxis(upAxisOption);
}

~AutoUpAxisChanger()
{
try {
_CleanupUpAxis();
} catch (const std::exception&) {
}
}

void cleanup() { _CleanupUpAxis(); }

private:
void _PrepareUpAxis(const TfToken& upAxisOption)
{
// If the user don't want to author the up-axis, we won't need to change the Maya up-axis.
const bool wantAuthorUpAxis = (upAxisOption != UsdMayaJobExportArgsTokens->none);
if (!wantAuthorUpAxis)
return;

// If the user want the up-axis authored in USD, well, author it.
const bool wantMayaPrefs = (upAxisOption == UsdMayaJobExportArgsTokens->mayaPrefs);
const bool isMayaUpAxisZ = MGlobal::isZAxisUp();
_wantUpAxisZ
= (wantMayaPrefs && isMayaUpAxisZ) || (upAxisOption == UsdMayaJobExportArgsTokens->z);
UsdGeomSetStageUpAxis(_stage, _wantUpAxisZ ? UsdGeomTokens->z : UsdGeomTokens->y);

// If the Maya up-axis is already the right one, we dont have to modify the Maya scene.
if (_wantUpAxisZ == isMayaUpAxisZ)
return;

// We need to modify the up-axis. Record if it was successful or not.
_modifiedUpAxis = _RotateUpAxis(_wantUpAxisZ);
}

void _CleanupUpAxis()
{
// If we did not modify the Maya up-axis, we don't have to restore it.
if (!_modifiedUpAxis)
return;

// Make sure cleanup will not be done twice even if there are exceptions.
_modifiedUpAxis = false;

// We are restoring the Maya scene up-axis, so we pass the opposite of the up-axis flag.
_RotateUpAxis(!_wantUpAxisZ);
}

static bool _RotateUpAxis(const bool wantUpAxisZ)
{
static const char* fullScriptFormat
= "proc rotateAllNodes(int $angle) {\n"
// Preserve the selection. Grouping and ungrouping changes it.
" string $selection[] = `ls -selection`;\n"
// Find all root nodes.
" string $rootNodeNames[] = `ls -assemblies`;\n"
// Group all root node under a new group:
//
// - Use -absolute to keep the grouped node world positions
// - Use -world to create the group under the root ofthe scene
// if the import was done at the root of the scene
// - Capture the new group name in a MEL variable called $groupName
" string $groupName = `group -absolute -world $rootNodeNames`;\n"
// Rotate the group to align with the desired axis.
//
// - Use relative rotation since we want to rotate the group as it is already
// positioned
// - Use -euler to make teh angle be relative to the current angle
// - Use forceOrderXYZ to force the rotation to be relative to world
// - Use -pivot to make sure we are rotating relative to the origin
// (The group is positioned at the center of all sub-object, so we need to
// specify the pivot)
" rotate -relative -euler -pivot 0 0 0 -forceOrderXYZ $angle 0 0 $groupName;\n"
// Ungroup while preserving the rotation.
" ungroup -absolute $groupName;\n"
// Restore the selection.
" select -replace $selection;\n"
"}\n"
"rotateAllNodes(%d);\n";

const int angleYtoZ = 90;
const int angleZtoY = -90;
const std::string fullScript
= TfStringPrintf(fullScriptFormat, wantUpAxisZ ? angleYtoZ : angleZtoY);

if (!MGlobal::executeCommand(fullScript.c_str())) {
MGlobal::displayWarning(
"Failed to temporarily rotate the scene to export with modified up-axis.");
return false;
}

return true;
}

UsdStageRefPtr _stage;
bool _wantUpAxisZ { false };
bool _modifiedUpAxis { false };
};

bool UsdMaya_WriteJob::Write(const std::string& fileName, bool append)
{
const std::vector<double>& timeSamples = mJobCtx.mArgs.timeSamples;
Expand Down Expand Up @@ -159,7 +265,9 @@ bool UsdMaya_WriteJob::Write(const std::string& fileName, bool append)
if (!_FinishWriting()) {
return false;
}

progressBar.advance();

return true;
}

Expand Down Expand Up @@ -328,6 +436,9 @@ bool UsdMaya_WriteJob::_BeginWriting(const std::string& fileName, bool append)
mJobCtx.mStage->SetFramesPerSecond(UsdMayaUtil::GetSceneMTimeUnitAsDouble());
}

// Temporarily change Maya's up-axis if needed.
_autoAxisChanger = std::make_unique<AutoUpAxisChanger>(mJobCtx.mStage, mJobCtx.mArgs.upAxis);

// Set the customLayerData on the layer
if (!mJobCtx.mArgs.customLayerData.empty()) {
mJobCtx.mStage->GetRootLayer()->SetCustomLayerData(mJobCtx.mArgs.customLayerData);
Expand Down Expand Up @@ -593,16 +704,6 @@ bool UsdMaya_WriteJob::_FinishWriting()
}
progressBar.advance();

// Unfortunately, MGlobal::isZAxisUp() is merely session state that does
// not get recorded in Maya files, so we cannot rely on it being set
// properly. Since "Y" is the more common upAxis, we'll just use
// isZAxisUp as an override to whatever our pipeline is configured for.
TfToken upAxis = UsdGeomGetFallbackUpAxis();
if (MGlobal::isZAxisUp()) {
upAxis = UsdGeomTokens->z;
}
UsdGeomSetStageUpAxis(mJobCtx.mStage, upAxis);

// XXX Currently all distance values are written directly to USD, and will
// be in centimeters (Maya's internal unit) despite what the users UIUnit
// preference is.
Expand Down Expand Up @@ -659,6 +760,9 @@ bool UsdMaya_WriteJob::_FinishWriting()
_PruneEmpties();
progressBar.advance();

// Restore Maya's up-axis if needed.
_autoAxisChanger.reset();

TF_STATUS("Saving stage");
if (mJobCtx.mStage->GetRootLayer()->PermissionToSave()) {
mJobCtx.mStage->GetRootLayer()->Save();
Expand Down
2 changes: 2 additions & 0 deletions lib/mayaUsd/fileio/jobs/writeJob.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
PXR_NAMESPACE_OPEN_SCOPE

class UsdMaya_ModelKindProcessor;
class AutoUpAxisChanger;

class UsdMaya_WriteJob
{
Expand Down Expand Up @@ -110,6 +111,7 @@ class UsdMaya_WriteJob
UsdMayaWriteJobContext mJobCtx;

std::unique_ptr<UsdMaya_ModelKindProcessor> _modelKindProcessor;
std::unique_ptr<AutoUpAxisChanger> _autoAxisChanger;
};

PXR_NAMESPACE_CLOSE_SCOPE
Expand Down
4 changes: 4 additions & 0 deletions lib/mayaUsd/python/wrapPrimWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ void wrapJobExportArgs()
.def_readonly("file", &UsdMayaJobExportArgs::file)
.def_readonly("rootPrim", &UsdMayaJobExportArgs::rootPrim)
.def_readonly("rootPrimType", &UsdMayaJobExportArgs::rootPrimType)
.def_readonly("upAxis", &UsdMayaJobExportArgs::upAxis)
.add_property(
"filteredTypeIds",
make_getter(
Expand Down Expand Up @@ -607,6 +608,9 @@ void wrapJobExportArgs()
"rootPrimType",
make_getter(
&UsdMayaJobExportArgs::rootPrimType, return_value_policy<return_by_value>()))
.add_property(
"upAxis",
make_getter(&UsdMayaJobExportArgs::upAxis, return_value_policy<return_by_value>()))
.def_readonly("pythonPerFrameCallback", &UsdMayaJobExportArgs::pythonPerFrameCallback)
.def_readonly("pythonPostCallback", &UsdMayaJobExportArgs::pythonPostCallback)
.add_property(
Expand Down
2 changes: 1 addition & 1 deletion lib/mayaUsd/resources/scripts/mayaUsdCacheMayaReference.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ def fileOptionsTabPage(tabLayout):
fileOptionsScroll = cmds.columnLayout('fileOptionsScroll')
optionsText = mayaUsdOptions.convertOptionsDictToText(cacheToUsd.loadCacheCreationOptions())
optionsText = mayaUsdOptions.setAnimateOption(_mayaRefDagPath, optionsText)
mel.eval('mayaUsdTranslatorExport("fileOptionsScroll", "post={exportOpts}", "{cacheOpts}", "")'.format(exportOpts=kTranslatorExportOptions, cacheOpts=optionsText))
mel.eval('mayaUsdTranslatorExport("fileOptionsScroll", "post={exportOpts};cacheToUSD", "{cacheOpts}", "")'.format(exportOpts=kTranslatorExportOptions, cacheOpts=optionsText))

cacheFileUsdHierarchyOptions(topForm)

Expand Down
11 changes: 11 additions & 0 deletions plugin/adsk/scripts/mayaUSDRegisterStrings.mel
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,17 @@ global proc mayaUSDRegisterStrings()
register("kExportDefaultPrimNoneLbl", "None");
register("kExportDefaultPrimAnn", "As part of its metadata, each USD stage can identify a default prim.\nThis is the primitive that is referenced in if you reference in a file.");

register("kExportAxisAndUnitLbl", "Axis & Unit Conversion");
register("kExportUpAxisLbl", "Up Axis");
register("kExportUpAxisAnn", "<b.</b>Select the up axis for the export file." +
"Rotation will be applied if converting to a different axis." +
"<b>None</b>: do not author upAxis." +
"<b>Use Maya Preferences</b>: use the axis of the current scene.");
register("kExportUpAxisNoneLbl", "None");
register("kExportUpAxisMayaPrefsLbl", "Use Maya Preferences");
register("kExportUpAxisYLbl", "Y");
register("kExportUpAxisZLbl", "Z");

// All strings for import dialog:
register("kImportAnimationDataLbl", "Animation Data");
register("kImportCustomFrameRangeLbl", "Custom Frame Range");
Expand Down
Loading