Skip to content

Commit

Permalink
Merge pull request #3851 from Autodesk/bailp/EMSUSD-1160/convert-pivot
Browse files Browse the repository at this point in the history
EMSUSD-1160 improve Maya pivot support
  • Loading branch information
seando-adsk authored Jul 18, 2024
2 parents bc3bd84 + 133d5ca commit 036c9d2
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 53 deletions.
169 changes: 127 additions & 42 deletions lib/mayaUsd/ufe/trf/UsdTransform3dMayaXformStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,12 +374,95 @@ MAYAUSD_VERIFY_CLASS_SETUP(UsdUfe::UsdTransform3dBase, UsdTransform3dMayaXformSt
UsdTransform3dMayaXformStack::UsdTransform3dMayaXformStack(const UsdUfe::UsdSceneItem::Ptr& item)
: UsdUfe::UsdTransform3dBase(item)
, _xformable(prim())
, _needPivotConversion(isPivotConversionNeeded())
{
if (!TF_VERIFY(_xformable)) {
throw std::runtime_error("Invalid scene item for transform stack");
}
}

bool UsdTransform3dMayaXformStack::isPivotConversionNeeded() const
{
// Note: USD and Maya use different pivots: USD has a single pivot that is used
// for both translation and scale, while Maya has separate ones. When working
// in this Maya transform stack mode, the USD pivot affects the position of
// the manipulators, so we need to convert it to a Maya-style pivot.
// Otherwise, prim with USD-style pivot won't work with the "center pivot"
// command. They would also not work well with the universal manipulator.
TfToken pivotName
= UsdGeomXformOp::GetOpName(UsdGeomXformOp::TypeTranslate, UsdGeomTokens->pivot);
auto pivotAttr = prim().GetAttribute(pivotName);
if (!pivotAttr)
return false;

if (!pivotAttr.HasAuthoredValue())
return false;

Ufe::Vector3d pivot = getVector3d<GfVec3f>(pivotName);
if (isAlmostZero(pivot))
return false;

return true;
}

void UsdTransform3dMayaXformStack::convertToMayaPivotIfNeeded()
{
if (!_needPivotConversion)
return;

// Note: must reset flag immediately because we call functions that would trigger
// conversion again, resulting in infinite recursion.
_needPivotConversion = false;

// Extract and clear the USD common pivot. The exiesting pivot can be authored
// with any precision, so we need to convert it if needed.
GfVec3f commonPivotValue;
{
TfToken pivotName
= UsdGeomXformOp::GetOpName(UsdGeomXformOp::TypeTranslate, UsdGeomTokens->pivot);
UsdAttribute pivotAttr = prim().GetAttribute(pivotName);

VtValue currentValue;
if (!pivotAttr.Get(&currentValue, getTime(path())))
return;

if (currentValue.IsHolding<GfVec3f>()) {
commonPivotValue = currentValue.UncheckedGet<GfVec3f>();
} else if (currentValue.IsHolding<GfVec3d>()) {
auto val = currentValue.UncheckedGet<GfVec3d>();
commonPivotValue.Set(val[0], val[1], val[2]);
} else if (currentValue.IsHolding<GfVec3h>()) {
auto val = currentValue.UncheckedGet<GfVec3h>();
commonPivotValue.Set(val[0], val[1], val[2]);
} else {
commonPivotValue.Set(0, 0, 0);
}
pivotAttr.Set(GfVec3f(0, 0, 0));
}

// Adjust possibly existing Maya rotate pivot by the common pivot.
// Note: must explicitly qualify the call to rotatePivot because
// the overload on this class hides the other.
{
Ufe::Vector3d currentPivotValue = rotatePivot();
Ufe::Transform3d::rotatePivot(
currentPivotValue.x() + commonPivotValue[0],
currentPivotValue.y() + commonPivotValue[1],
currentPivotValue.z() + commonPivotValue[2]);
}

// Adjust possibly existing Maya scale pivot by the common pivot.
// Note: must explicitly qualify the call to scalePivot because
// the overload on this class hides the other.
{
Ufe::Vector3d currentPivotValue = scalePivot();
Ufe::Transform3d::scalePivot(
currentPivotValue.x() + commonPivotValue[0],
currentPivotValue.y() + commonPivotValue[1],
currentPivotValue.z() + commonPivotValue[2]);
}
}

/* static */
UsdTransform3dMayaXformStack::Ptr
UsdTransform3dMayaXformStack::create(const UsdUfe::UsdSceneItem::Ptr& item)
Expand Down Expand Up @@ -438,6 +521,8 @@ bool UsdTransform3dMayaXformStack::isFallback() const { return false; }
Ufe::RotateUndoableCommand::Ptr
UsdTransform3dMayaXformStack::rotateCmd(double x, double y, double z)
{
convertToMayaPivotIfNeeded();

UsdGeomXformOp op;
TfToken attrName;
bool hasRotate = hasOp(NdxRotate);
Expand Down Expand Up @@ -506,6 +591,8 @@ UsdTransform3dMayaXformStack::rotateCmd(double x, double y, double z)

Ufe::ScaleUndoableCommand::Ptr UsdTransform3dMayaXformStack::scaleCmd(double x, double y, double z)
{
convertToMayaPivotIfNeeded();

UsdGeomXformOp op;
TfToken attrName;
if (hasOp(NdxScale)) {
Expand Down Expand Up @@ -552,49 +639,59 @@ Ufe::ScaleUndoableCommand::Ptr UsdTransform3dMayaXformStack::scaleCmd(double x,
Ufe::TranslateUndoableCommand::Ptr
UsdTransform3dMayaXformStack::rotatePivotCmd(double x, double y, double z)
{
convertToMayaPivotIfNeeded();

return pivotCmd(getOpSuffix(NdxRotatePivot), x, y, z);
}

Ufe::Vector3d UsdTransform3dMayaXformStack::rotatePivot() const
{
return getVector3d<GfVec3f>(
Ufe::Vector3d mayaPivot = getVector3d<GfVec3f>(
UsdGeomXformOp::GetOpName(UsdGeomXformOp::TypeTranslate, getOpSuffix(NdxRotatePivot)));

if (_needPivotConversion) {
Ufe::Vector3d commonPivot = getVector3d<GfVec3f>(
UsdGeomXformOp::GetOpName(UsdGeomXformOp::TypeTranslate, UsdGeomTokens->pivot));

mayaPivot = Ufe::Vector3d(
commonPivot.x() + mayaPivot.x(),
commonPivot.y() + mayaPivot.y(),
commonPivot.z() + mayaPivot.z());
}

return mayaPivot;
}

Ufe::TranslateUndoableCommand::Ptr
UsdTransform3dMayaXformStack::scalePivotCmd(double x, double y, double z)
{
convertToMayaPivotIfNeeded();

return pivotCmd(getOpSuffix(NdxScalePivot), x, y, z);
}

Ufe::Vector3d UsdTransform3dMayaXformStack::scalePivot() const
{
return getVector3d<GfVec3f>(
Ufe::Vector3d mayaPivot = getVector3d<GfVec3f>(
UsdGeomXformOp::GetOpName(UsdGeomXformOp::TypeTranslate, getOpSuffix(NdxScalePivot)));

if (_needPivotConversion) {
Ufe::Vector3d commonPivot = getVector3d<GfVec3f>(
UsdGeomXformOp::GetOpName(UsdGeomXformOp::TypeTranslate, UsdGeomTokens->pivot));

mayaPivot = Ufe::Vector3d(
commonPivot.x() + mayaPivot.x(),
commonPivot.y() + mayaPivot.y(),
commonPivot.z() + mayaPivot.z());
}

return mayaPivot;
}

Ufe::TranslateUndoableCommand::Ptr
UsdTransform3dMayaXformStack::translateRotatePivotCmd(double x, double y, double z)
{
// Note: USD and Maya use different pivots: USD has a single pivot that is used
// for both translation and scale, while Maya has separate ones. When working
// in this Maya transform stack mode, the USD pivot affects the position of
// the manipulators, so we need to take it into account here.
//
// Otherwise, prim with USD-style picot won't work with the "center pivot"
// command.
TfToken commonPivotName
= UsdGeomXformOp::GetOpName(UsdGeomXformOp::TypeTranslate, UsdGeomTokens->pivot);
auto commonPivotAttr = prim().GetAttribute(commonPivotName);
if (commonPivotAttr && commonPivotAttr.HasAuthoredValue()) {
Ufe::Vector3d commonPivot = getVector3d<GfVec3f>(commonPivotName);
if (!isAlmostZero(commonPivot)) {
x = commonPivot.x() - x;
y = commonPivot.y() - y;
z = commonPivot.z() - z;
return setVector3dCmd(GfVec3f(x, y, z), commonPivotName);
}
}
convertToMayaPivotIfNeeded();

auto opSuffix = getOpSuffix(NdxRotatePivotTranslate);
auto attrName = UsdGeomXformOp::GetOpName(UsdGeomXformOp::TypeTranslate, opSuffix);
Expand All @@ -603,29 +700,15 @@ UsdTransform3dMayaXformStack::translateRotatePivotCmd(double x, double y, double

Ufe::Vector3d UsdTransform3dMayaXformStack::rotatePivotTranslation() const
{
// Note: USD and Maya use different pivots: USD has a single pivot that is used
// for both translation and scale, while Maya has separate ones. When working
// in this Maya transform stack mode, the USD pivot is only used to move the
// position of the manipulators, by returning it as part of this function.
//
// Interestingly, this is correct and enough to both display the manip at the
// correct position in the viewport *and* give the correct results. That's
// because USD internally will apply its pivot and the manip will give the
// correct value when manipulating.
Ufe::Vector3d commonPivot = getVector3d<GfVec3f>(
UsdGeomXformOp::GetOpName(UsdGeomXformOp::TypeTranslate, UsdGeomTokens->pivot));
Ufe::Vector3d mayaPivot = getVector3d<GfVec3f>(UsdGeomXformOp::GetOpName(
return getVector3d<GfVec3f>(UsdGeomXformOp::GetOpName(
UsdGeomXformOp::TypeTranslate, getOpSuffix(NdxRotatePivotTranslate)));
mayaPivot.set(
mayaPivot.x() + commonPivot.x(),
mayaPivot.y() + commonPivot.y(),
mayaPivot.z() + commonPivot.z());
return mayaPivot;
}

Ufe::TranslateUndoableCommand::Ptr
UsdTransform3dMayaXformStack::translateScalePivotCmd(double x, double y, double z)
{
convertToMayaPivotIfNeeded();

auto opSuffix = getOpSuffix(NdxScalePivotTranslate);
auto attrName = UsdGeomXformOp::GetOpName(UsdGeomXformOp::TypeTranslate, opSuffix);
return setVector3dCmd(GfVec3f(x, y, z), attrName, opSuffix);
Expand Down Expand Up @@ -748,6 +831,8 @@ UsdTransform3dMayaXformStack::pivotCmd(const TfToken& pvtOpSuffix, double x, dou
Ufe::SetMatrix4dUndoableCommand::Ptr
UsdTransform3dMayaXformStack::setMatrixCmd(const Ufe::Matrix4d& m)
{
convertToMayaPivotIfNeeded();

// Note: UsdSetMatrix4dUndoableCommand uses separate calls to translate, rotate and scale,
// so check those 3 attributes.
const TfToken attrs[]
Expand Down Expand Up @@ -865,11 +950,11 @@ Ufe::Transform3d::Ptr UsdTransform3dMayaXformStackHandler::editTransform3d(
// MAYA-109190: Moved the IsInstanceProxy() check here since it was causing the
// camera framing not properly be applied.
//
// HS January 15, 2021: After speaking with Pierre, there is a more robust solution to move this
// check entirely from here.
// HS January 15, 2021: After speaking with Pierre, there is a more robust solution to move
// this check entirely from here.

// According to USD docs, editing scene description via instance proxies and their properties is
// not allowed.
// According to USD docs, editing scene description via instance proxies and their
// properties is not allowed.
// https://graphics.pixar.com/usd/docs/api/_usd__page__scenegraph_instancing.html#Usd_ScenegraphInstancing_InstanceProxies
auto usdItem = downcast(item);
if (usdItem->prim().IsInstanceProxy()) {
Expand Down
4 changes: 4 additions & 0 deletions lib/mayaUsd/ufe/trf/UsdTransform3dMayaXformStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,11 @@ class MAYAUSD_CORE_PUBLIC UsdTransform3dMayaXformStack : public UsdUfe::UsdTrans
const PXR_NS::TfToken& attrName,
const PXR_NS::TfToken& opSuffix = PXR_NS::TfToken());

bool isPivotConversionNeeded() const;
void convertToMayaPivotIfNeeded();

PXR_NS::UsdGeomXformable _xformable;
bool _needPivotConversion { false };

private:
Ufe::TranslateUndoableCommand::Ptr
Expand Down
1 change: 1 addition & 0 deletions test/lib/ufe/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ set(TEST_SCRIPT_FILES
testRotateCmd.py
testRotateCmdUndoRedo.py
testRotatePivot.py
testPivotConversion.py
testScaleCmd.py
testSceneItem.py
testSelection.py
Expand Down
20 changes: 9 additions & 11 deletions test/lib/ufe/testCenterPivot.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,6 @@ def setUp(self):
self.assertTrue(self.pluginsLoaded)


def checkPos(self, m, p):
self.assertAlmostEqual(m[ndx(3,0)], p[0])
self.assertAlmostEqual(m[ndx(3,1)], p[1])
self.assertAlmostEqual(m[ndx(3,2)], p[2])

def testCenterPivot(self):
'''Verify the behavior Transform3d UFE pivot interface.
Expand Down Expand Up @@ -123,7 +118,7 @@ def verifyPointInstancerPosition():

def testCenterPivotWithUSDPivot(self):
'''
Test centering the pivot when teh USD file has a USD-style pivot,
Test centering the pivot when the USD file has a USD-style pivot,
not a Maya-style pivot.
UFE Feature : Transform3d
Expand Down Expand Up @@ -174,7 +169,7 @@ def verifyCubeUSDPivots(trPivot, tPivot, tsPivot, tstPivot):

verifyCubeUSDPivots(
[2., -3., -1.], # translate rotate pivot
[7., 7., 8.], # translate pivot (equivalent to translate rotate translate pivot)
[7., 7., 8.], # USD translate pivot (equivalent to translate rotate translate pivot)
[2., -3., -1.], # translate scale pivot
[0., 0., 0.] # translate scale translate pivot
)
Expand All @@ -187,11 +182,14 @@ def verifyCubeUFEPivots(trPivot, trtPivot, tsPivot, tstPivot):
assertVectorAlmostEqual(self, cubeUfeT3d.scalePivot().vector, tsPivot)
assertVectorAlmostEqual(self, cubeUfeT3d.scalePivotTranslation().vector, tstPivot)

# Note: when going through UFE, the USD translate pivot is added to the Maya
# pivots (translate and scale). So the USD translate pivot that was verified
# above to be [7., 7., 8.] is instead added to the other pivot values.
verifyCubeUFEPivots(
[2., -3., -1.], # translate rotate pivot
[7., 7., 8.], # translate rotate translate pivot
[2., -3., -1.], # translate scale pivot
[0., 0., 0.] # translate scale translate pivot
[9., 4., 7.], # translate rotate pivot
[0., 0., 0.], # translate rotate translate pivot
[9., 4., 7.], # translate scale pivot
[0., 0., 0.] # translate scale translate pivot
)

# Select the prim.
Expand Down
Loading

0 comments on commit 036c9d2

Please sign in to comment.