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

Adds option to euler filter output transform data #299

Merged
merged 1 commit into from
Jul 16, 2018
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
16 changes: 16 additions & 0 deletions third_party/maya/lib/usdMaya/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ pxr_test_scripts(
testenv/testUsdExportCamera.py
testenv/testUsdExportColorSets.py
testenv/testUsdExportDisplayColor.py
testenv/testUsdExportEulerFilter.py
testenv/testUsdExportFilterTypes.py
testenv/testUsdExportFrameOffset.py
testenv/testUsdExportInstances.py
Expand Down Expand Up @@ -306,6 +307,21 @@ pxr_register_test(testUsdExportDisplayColor
MAYA_APP_DIR=<PXR_TEST_DIR>/maya_profile
)

pxr_install_test_dir(
SRC testenv/UsdExportEulerFilterTest
DEST testUsdExportEulerFilter
)
pxr_register_test(testUsdExportEulerFilter
CUSTOM_PYTHON "${MAYA_BASE_DIR}/bin/mayapy"
COMMAND "${CMAKE_INSTALL_PREFIX}/tests/testUsdExportEulerFilter"
TESTENV testUsdExportEulerFilter
ENV
MAYA_PLUG_IN_PATH=${CMAKE_INSTALL_PREFIX}/third_party/maya/plugin
MAYA_SCRIPT_PATH=${CMAKE_INSTALL_PREFIX}/third_party/maya/share/usd/plugins/usdMaya/resources
MAYA_DISABLE_CIP=1
MAYA_APP_DIR=<PXR_TEST_DIR>/maya_profile
)

pxr_install_test_dir(
SRC testenv/UsdExportFilterTypesTest
DEST testUsdExportFilterTypes
Expand Down
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 @@ -285,24 +318,24 @@ void MayaTransformWriter::_PushTransformStack(
}

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

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

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

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

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

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

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

// inspect the scale pivot point
bool hasScalePivot = _GatherAnimChannel(TRANSLATE, iTrans, "scalePivot", "X", "Y", "Z", &_animChannels, writeAnim, true);
bool hasScalePivot = _GatherAnimChannel(TRANSLATE, iTrans, PxrUsdMayaXformStackTokens->scalePivot, "X", "Y", "Z", &_animChannels, writeAnim, true);
if (hasScalePivot) {
scalePivotIdx = _animChannels.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", &_animChannels, writeAnim, true)) {
if (_GatherAnimChannel(SHEAR, iTrans, PxrUsdMayaXformStackTokens->shear, "XY", "XZ", "YZ", &_animChannels, writeAnim, true)) {
conformsToCommonAPI = false;
}

// add the scale. no suffix to be closer compatibility with common API
_GatherAnimChannel(SCALE, iTrans, "scale", "X", "Y", "Z", &_animChannels, writeAnim, false);
_GatherAnimChannel(SCALE, iTrans, PxrUsdMayaXformStackTokens->scale, "X", "Y", "Z", &_animChannels, 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;
_animChannels.push_back(chan);
scalePivotINVIdx = _animChannels.size()-1;
Expand Down Expand Up @@ -385,8 +418,8 @@ void MayaTransformWriter::_PushTransformStack(
// since no other ops have been found
//
// NOTE: scalePivotIdx > rotPivotINVIdx
_animChannels[rotPivotIdx].opName = "pivot";
_animChannels[scalePivotINVIdx].opName = "pivot";
_animChannels[rotPivotIdx].opName = PxrUsdMayaXformStackTokens->pivot;
_animChannels[scalePivotINVIdx].opName = PxrUsdMayaXformStackTokens->pivot;
_animChannels.erase(_animChannels.begin()+scalePivotIdx);
_animChannels.erase(_animChannels.begin()+rotPivotINVIdx);
}
Expand All @@ -398,7 +431,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 @@ -562,9 +595,8 @@ bool MayaTransformWriter::_WriteXformableAttrs(
// Write parent class attrs
_WriteImageableAttrs(_xformDagPath, usdTime, xformSchema); // for the shape

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

Expand Down
14 changes: 11 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 @@ -62,6 +67,8 @@ struct AnimChannel
class MayaTransformWriter : public MayaPrimWriter
{
public:
typedef std::unordered_map<const TfToken, MEulerRotation, TfToken::HashFunctor> TokenRotationMap;

PXRUSDMAYA_API
MayaTransformWriter(
const MDagPath& iDag,
Expand Down Expand Up @@ -125,6 +132,7 @@ class MayaTransformWriter : public MayaPrimWriter
std::vector<AnimChannel> _animChannels;
bool _isShapeAnimated;
bool _isInstanceSource;
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 @@ -222,6 +222,8 @@ PxrUsdMayaJobExportArgs::PxrUsdMayaJobExportArgs(
UsdGeomTokens->bilinear,
UsdGeomTokens->none
})),
eulerFilter(
_Boolean(userArgs, PxrUsdExportJobArgsTokens->eulerFilter)),
excludeInvisible(
_Boolean(userArgs, PxrUsdExportJobArgsTokens->renderableOnly)),
exportCollectionBasedBindings(
Expand Down Expand Up @@ -324,6 +326,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 @@ -430,6 +433,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 @@ -61,6 +61,7 @@ TF_DECLARE_PUBLIC_TOKENS(PxrUsdMayaTranslatorTokens,
(chaserArgs) \
(defaultCameras) \
(defaultMeshScheme) \
(eulerFilter) \
(exportCollectionBasedBindings) \
(exportColorSets) \
(exportDisplayColor) \
Expand Down Expand Up @@ -116,6 +117,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
Loading