Skip to content

Commit

Permalink
[maya] add option to euler filter transform rotations
Browse files Browse the repository at this point in the history
  • Loading branch information
pmolodo authored and sirpalee committed Jun 25, 2018
1 parent d52b9c8 commit 8457233
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 35 deletions.
96 changes: 64 additions & 32 deletions third_party/maya/lib/usdMaya/MayaTransformWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "usdMaya/primWriterRegistry.h"
#include "usdMaya/util.h"
#include "usdMaya/usdWriteJob.h"
#include "usdMaya/xformStack.h"

#include "pxr/usd/usdGeom/xform.h"
#include "pxr/usd/usdGeom/xformCommonAPI.h"
Expand Down Expand Up @@ -98,6 +99,8 @@ computeXFormOps(
const UsdGeomXformable& usdXformable,
const std::vector<AnimChannel>& animChanList,
const UsdTimeCode &usdTime,
bool eulerFilter,
MayaTransformWriter::TokenRotationMap& previousRotates,
UsdUtilsSparseValueWriter *valueWriter)
{
// Iterate over each AnimChannel, retrieve the default value and pull the
Expand All @@ -113,12 +116,7 @@ computeXFormOps(
bool hasAnimated = false, hasStatic = false;
for (unsigned int i = 0; i<3; i++) {
if (animChannel.sampleType[i] == ANIMATED) {
// NOTE the default value has already been converted to
// radians.
double chanVal = animChannel.plug[i].asDouble();
value[i] = animChannel.opType == ROTATE ?
GfRadiansToDegrees(chanVal) :
chanVal;
value[i] = animChannel.plug[i].asDouble();
hasAnimated = true;
}
else if (animChannel.sampleType[i] == STATIC) {
Expand All @@ -136,6 +134,40 @@ computeXFormOps(
// animating ones are actually animating
if ((usdTime == UsdTimeCode::Default() && hasStatic && !hasAnimated) ||
(usdTime != UsdTimeCode::Default() && hasAnimated)) {

if (animChannel.opType == ROTATE) {
if (hasAnimated && eulerFilter) {
const TfToken& lookupName = animChannel.opName.IsEmpty() ?
UsdGeomXformOp::GetOpTypeToken(animChannel.usdOpType) :
animChannel.opName;
auto findResult = previousRotates.find(lookupName);
if (findResult == previousRotates.end()) {
MEulerRotation::RotationOrder rotOrder =
PxrUsdMayaXformStack::RotateOrderFromOpType(
animChannel.usdOpType,
MEulerRotation::kXYZ);
MEulerRotation currentRotate(value[0], value[1], value[2], rotOrder);
previousRotates[lookupName] = currentRotate;
}
else {
MEulerRotation& previousRotate = findResult->second;
MEulerRotation::RotationOrder rotOrder =
PxrUsdMayaXformStack::RotateOrderFromOpType(
animChannel.usdOpType,
previousRotate.order);
MEulerRotation currentRotate(value[0], value[1], value[2], rotOrder);
currentRotate.setToClosestSolution(previousRotate);
for (unsigned int i = 0; i<3; i++) {
value[i] = currentRotate[i];
}
previousRotates[lookupName] = currentRotate;
}
}
for (unsigned int i = 0; i<3; i++) {
value[i] = GfRadiansToDegrees(value[i]);
}
}

setXformOp(animChannel.op, value, usdTime, valueWriter);
}
}
Expand All @@ -150,7 +182,7 @@ static bool
_GatherAnimChannel(
XFormOpType opType,
const MFnTransform& iTrans,
MString parentName,
const TfToken& parentName,
MString xName, MString yName, MString zName,
std::vector<AnimChannel>* oAnimChanList,
bool isWritingAnimation,
Expand All @@ -160,8 +192,9 @@ _GatherAnimChannel(
chan.opType = opType;
chan.isInverse = false;
if (setOpName) {
chan.opName = parentName.asChar();
chan.opName = parentName;
}
MString parentNameMStr = parentName.GetText();

// We default to single precision (later we set the main translate op and
// shear to double)
Expand All @@ -172,22 +205,22 @@ _GatherAnimChannel(
// this is to handle the case where there is a connection to the parent
// plug but not to the child plugs, if the connection is there and you are
// not forcing static, then all of the children are considered animated
int parentSample = PxrUsdMayaUtil::getSampledType(iTrans.findPlug(parentName),false);
int parentSample = PxrUsdMayaUtil::getSampledType(iTrans.findPlug(parentNameMStr),false);

// Determine what plug are needed based on default value & being
// connected/animated
MStringArray channels;
channels.append(parentName+xName);
channels.append(parentName+yName);
channels.append(parentName+zName);
channels.append(parentNameMStr+xName);
channels.append(parentNameMStr+yName);
channels.append(parentNameMStr+zName);

GfVec3d nullValue(opType == SCALE ? 1.0 : 0.0);
for (unsigned int i = 0; i<3; i++) {
// Find the plug and retrieve the data as the channel default value. It
// won't be updated if the channel is NOT ANIMATED
chan.plug[i] = iTrans.findPlug(channels[i]);
double plugValue = chan.plug[i].asDouble();
chan.defValue[i] = opType == ROTATE ? GfRadiansToDegrees(plugValue) : plugValue;
chan.defValue[i] = plugValue;
chan.sampleType[i] = NO_XFORM;
// If we allow animation and either the parentsample or local sample is
// not 0 then we havea ANIMATED sample else we have a scale and the
Expand All @@ -211,7 +244,7 @@ _GatherAnimChannel(
} else if (opType == TRANSLATE) {
chan.usdOpType = UsdGeomXformOp::TypeTranslate;
// The main translate is set to double precision
if (parentName == "translate") {
if (parentName == PxrUsdMayaXformStackTokens->translate) {
chan.precision = UsdGeomXformOp::PrecisionDouble;
}
} else if (opType == ROTATE) {
Expand All @@ -223,7 +256,7 @@ _GatherAnimChannel(
}
else {
// Rotation Order ONLY applies to the "rotate" attribute
if (parentName == "rotate") {
if (parentName == PxrUsdMayaXformStackTokens->rotate) {
switch (iTrans.rotationOrder()) {
case MTransformationMatrix::kYZX:
chan.usdOpType = UsdGeomXformOp::TypeRotateYZX;
Expand Down Expand Up @@ -288,24 +321,24 @@ void MayaTransformWriter::pushTransformStack(
}

// inspect the translate, no suffix to be closer compatibility with common API
_GatherAnimChannel(TRANSLATE, iTrans, "translate", "X", "Y", "Z", &mAnimChanList, writeAnim, false);
_GatherAnimChannel(TRANSLATE, iTrans, PxrUsdMayaXformStackTokens->translate, "X", "Y", "Z", &mAnimChanList, writeAnim, false);

// inspect the rotate pivot translate
if (_GatherAnimChannel(TRANSLATE, iTrans, "rotatePivotTranslate", "X", "Y", "Z", &mAnimChanList, writeAnim, true)) {
if (_GatherAnimChannel(TRANSLATE, iTrans, PxrUsdMayaXformStackTokens->rotatePivotTranslate, "X", "Y", "Z", &mAnimChanList, writeAnim, true)) {
conformsToCommonAPI = false;
}

// inspect the rotate pivot
bool hasRotatePivot = _GatherAnimChannel(TRANSLATE, iTrans, "rotatePivot", "X", "Y", "Z", &mAnimChanList, writeAnim, true);
bool hasRotatePivot = _GatherAnimChannel(TRANSLATE, iTrans, PxrUsdMayaXformStackTokens->rotatePivot, "X", "Y", "Z", &mAnimChanList, writeAnim, true);
if (hasRotatePivot) {
rotPivotIdx = mAnimChanList.size()-1;
}

// inspect the rotate, no suffix to be closer compatibility with common API
_GatherAnimChannel(ROTATE, iTrans, "rotate", "X", "Y", "Z", &mAnimChanList, writeAnim, false);
_GatherAnimChannel(ROTATE, iTrans, PxrUsdMayaXformStackTokens->rotate, "X", "Y", "Z", &mAnimChanList, writeAnim, false);

// inspect the rotateAxis/orientation
if (_GatherAnimChannel(ROTATE, iTrans, "rotateAxis", "X", "Y", "Z", &mAnimChanList, writeAnim, true)) {
if (_GatherAnimChannel(ROTATE, iTrans, PxrUsdMayaXformStackTokens->rotateAxis, "X", "Y", "Z", &mAnimChanList, writeAnim, true)) {
conformsToCommonAPI = false;
}

Expand All @@ -314,37 +347,37 @@ void MayaTransformWriter::pushTransformStack(
AnimChannel chan;
chan.usdOpType = UsdGeomXformOp::TypeTranslate;
chan.precision = UsdGeomXformOp::PrecisionFloat;
chan.opName = "rotatePivot";
chan.opName = PxrUsdMayaXformStackTokens->rotatePivot;
chan.isInverse = true;
mAnimChanList.push_back(chan);
rotPivotINVIdx = mAnimChanList.size()-1;
}

// inspect the scale pivot translation
if (_GatherAnimChannel(TRANSLATE, iTrans, "scalePivotTranslate", "X", "Y", "Z", &mAnimChanList, writeAnim, true)) {
if (_GatherAnimChannel(TRANSLATE, iTrans, PxrUsdMayaXformStackTokens->scalePivotTranslate, "X", "Y", "Z", &mAnimChanList, writeAnim, true)) {
conformsToCommonAPI = false;
}

// inspect the scale pivot point
bool hasScalePivot = _GatherAnimChannel(TRANSLATE, iTrans, "scalePivot", "X", "Y", "Z", &mAnimChanList, writeAnim, true);
bool hasScalePivot = _GatherAnimChannel(TRANSLATE, iTrans, PxrUsdMayaXformStackTokens->scalePivot, "X", "Y", "Z", &mAnimChanList, writeAnim, true);
if (hasScalePivot) {
scalePivotIdx = mAnimChanList.size()-1;
}

// inspect the shear. Even if we have one xform on the xform list, it represents a share so we should name it
if (_GatherAnimChannel(SHEAR, iTrans, "shear", "XY", "XZ", "YZ", &mAnimChanList, writeAnim, true)) {
if (_GatherAnimChannel(SHEAR, iTrans, PxrUsdMayaXformStackTokens->shear, "XY", "XZ", "YZ", &mAnimChanList, writeAnim, true)) {
conformsToCommonAPI = false;
}

// add the scale. no suffix to be closer compatibility with common API
_GatherAnimChannel(SCALE, iTrans, "scale", "X", "Y", "Z", &mAnimChanList, writeAnim, false);
_GatherAnimChannel(SCALE, iTrans, PxrUsdMayaXformStackTokens->scale, "X", "Y", "Z", &mAnimChanList, writeAnim, false);

// inverse the scale pivot point
if (hasScalePivot) {
AnimChannel chan;
chan.usdOpType = UsdGeomXformOp::TypeTranslate;
chan.precision = UsdGeomXformOp::PrecisionFloat;
chan.opName = "scalePivot";
chan.opName = PxrUsdMayaXformStackTokens->scalePivot;
chan.isInverse = true;
mAnimChanList.push_back(chan);
scalePivotINVIdx = mAnimChanList.size()-1;
Expand Down Expand Up @@ -388,8 +421,8 @@ void MayaTransformWriter::pushTransformStack(
// since no other ops have been found
//
// NOTE: scalePivotIdx > rotPivotINVIdx
mAnimChanList[rotPivotIdx].opName = "pivot";
mAnimChanList[scalePivotINVIdx].opName = "pivot";
mAnimChanList[rotPivotIdx].opName = PxrUsdMayaXformStackTokens->pivot;
mAnimChanList[scalePivotINVIdx].opName = PxrUsdMayaXformStackTokens->pivot;
mAnimChanList.erase(mAnimChanList.begin()+scalePivotIdx);
mAnimChanList.erase(mAnimChanList.begin()+rotPivotINVIdx);
}
Expand All @@ -401,7 +434,7 @@ void MayaTransformWriter::pushTransformStack(
AnimChannel& animChan = *iter;
animChan.op = usdXformable.AddXformOp(
animChan.usdOpType, animChan.precision,
TfToken(animChan.opName),
animChan.opName,
animChan.isInverse);
}
}
Expand Down Expand Up @@ -565,9 +598,8 @@ bool MayaTransformWriter::writeTransformAttrs(
// Write parent class attrs
writePrimAttrs(mXformDagPath, usdTime, xformSchema); // for the shape

// can this use xformSchema instead? do we even need _usdXform?
computeXFormOps(xformSchema, mAnimChanList, usdTime,
_GetSparseValueWriter());
computeXFormOps(xformSchema, mAnimChanList, usdTime, getArgs().eulerFilter,
previousRotates, _GetSparseValueWriter());
return true;
}

Expand Down
13 changes: 10 additions & 3 deletions third_party/maya/lib/usdMaya/MayaTransformWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@

#include "pxr/usd/usdGeom/xform.h"
#include "pxr/usd/usdGeom/xformOp.h"
#include <maya/MEulerRotation.h>
#include <maya/MFnTransform.h>
#include <maya/MPlugArray.h>

#include <unordered_map>

PXR_NAMESPACE_OPEN_SCOPE

class UsdGeomXformable;
Expand All @@ -46,13 +49,15 @@ struct AnimChannel
{
MPlug plug[3];
AnimChannelSampleType sampleType[3];
// defValue should always be in "usd" space. that is, if it's a rotation
// it should be a degree not radians.
// defValue should always be in "maya" space. that is, if it's a rotation
// it should be radians, not degrees. (This is done so we only need to do
// conversion in one place, and so that, if we need to do euler filtering,
// we don't to do conversions, and then undo them to use MEulerRotation).
GfVec3d defValue;
XFormOpType opType;
UsdGeomXformOp::Type usdOpType;
UsdGeomXformOp::Precision precision;
std::string opName;
TfToken opName;
bool isInverse;
UsdGeomXformOp op;
};
Expand All @@ -61,6 +66,7 @@ struct AnimChannel
class MayaTransformWriter : public MayaPrimWriter
{
public:
typedef std::unordered_map<const TfToken, MEulerRotation, TfToken::HashFunctor> TokenRotationMap;

PXRUSDMAYA_API
MayaTransformWriter(
Expand Down Expand Up @@ -103,6 +109,7 @@ class MayaTransformWriter : public MayaPrimWriter
bool mIsShapeAnimated;
std::vector<AnimChannel> mAnimChanList;
bool mIsInstanceSource;
TokenRotationMap previousRotates;
};

typedef std::shared_ptr<MayaTransformWriter> MayaTransformWriterPtr;
Expand Down
4 changes: 4 additions & 0 deletions third_party/maya/lib/usdMaya/jobArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ PxrUsdMayaJobExportArgs::PxrUsdMayaJobExportArgs(
UsdGeomTokens->bilinear,
UsdGeomTokens->none
})),
eulerFilter(
_Boolean(userArgs, PxrUsdExportJobArgsTokens->eulerFilter)),
excludeInvisible(
_Boolean(userArgs, PxrUsdExportJobArgsTokens->renderableOnly)),
exportCollectionBasedBindings(
Expand Down Expand Up @@ -321,6 +323,7 @@ operator <<(std::ostream& out, const PxrUsdMayaJobExportArgs& exportArgs)
<< "mergeTransformAndShape: " << TfStringify(exportArgs.mergeTransformAndShape) << std::endl
<< "exportInstances: " << TfStringify(exportArgs.exportInstances) << std::endl
<< "timeInterval: " << exportArgs.timeInterval << std::endl
<< "eulerFilter: " << TfStringify(exportArgs.eulerFilter) << std::endl
<< "excludeInvisible: " << TfStringify(exportArgs.excludeInvisible) << std::endl
<< "exportDefaultCameras: " << TfStringify(exportArgs.exportDefaultCameras) << std::endl
<< "exportSkels: " << TfStringify(exportArgs.exportSkels) << std::endl
Expand Down Expand Up @@ -423,6 +426,7 @@ const VtDictionary& PxrUsdMayaJobExportArgs::GetDefaultDictionary()
d[PxrUsdExportJobArgsTokens->shadingMode] =
PxrUsdMayaShadingModeTokens->displayColor.GetString();
d[PxrUsdExportJobArgsTokens->stripNamespaces] = false;
d[PxrUsdExportJobArgsTokens->eulerFilter] = false;

// plugInfo.json site defaults.
// The defaults dict should be correctly-typed, so enable
Expand Down
2 changes: 2 additions & 0 deletions third_party/maya/lib/usdMaya/jobArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ TF_DECLARE_PUBLIC_TOKENS(PxrUsdMayaTranslatorTokens,
(chaserArgs) \
(defaultCameras) \
(defaultMeshScheme) \
(eulerFilter) \
(exportCollectionBasedBindings) \
(exportColorSets) \
(exportDisplayColor) \
Expand Down Expand Up @@ -113,6 +114,7 @@ TF_DECLARE_PUBLIC_TOKENS(PxrUsdImportJobArgsTokens,
struct PxrUsdMayaJobExportArgs
{
const TfToken defaultMeshScheme;
const bool eulerFilter;
const bool excludeInvisible;
const bool exportCollectionBasedBindings;
const bool exportColorSets;
Expand Down
3 changes: 3 additions & 0 deletions third_party/maya/lib/usdMaya/usdExport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ MSyntax usdExport::createSyntax()
syntax.addFlag("-sn",
PxrUsdExportJobArgsTokens->stripNamespaces.GetText(),
MSyntax::kBoolean);
syntax.addFlag("-ef" ,
PxrUsdExportJobArgsTokens->eulerFilter.GetText(),
MSyntax::kBoolean);
syntax.addFlag("-dms",
PxrUsdExportJobArgsTokens->defaultMeshScheme.GetText(),
MSyntax::kString);
Expand Down

0 comments on commit 8457233

Please sign in to comment.