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

[maya] factor out matching of xforms into PxrUsdMayaXformStack #287

Merged
merged 1 commit into from
Nov 18, 2017

Conversation

pmolodo
Copy link
Contributor

@pmolodo pmolodo commented Sep 19, 2017

Description of Change(s)

This factors out the code for defining standardized transform stacks into a new class, xformStack. This class is also responsible for checking compatibility with existing usd xform stacks. This code is also exposed through the API for use externally.

There's actually nothing in the code which is specific to maya, so in theory it could be promoted to somewhere in the usd core, but it may be premature if it's not used anywhere else yet... and, of course, I'm sure that would warrant a higher level of scrutiny.

Speaking of scrutiny - I'm planning to add some unit tests, but wanted to give a chance for comment now!

@jtran56
Copy link

jtran56 commented Sep 19, 2017

Filed as internal issue #151051.

@pmolodo
Copy link
Contributor Author

pmolodo commented Oct 6, 2017

Just pushed an update for this PR - mostly just adds python wrappers and a bunch of tests (though also renames one of the class aliases).

The commit in this PR is the consolidated version, if you want to see the incremental changes since the last push, you can see them here:

d663105
14028e0
00b102e
2148a40
8c17e76

Copy link
Contributor

@sunyab sunyab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @elrond79, passing along code review notes from the sets team. We'd be happy to chat more about the comments re: FirstMatchingSubstack if that'd be helpful. Thanks!

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=./maya_profile
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we remove the MAYA_APP_DIR in this test registration for now, pending a resolution to the issues in #209?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, that was accidentally included. I'll get rid of it.

return std::vector<OpClassPtr>();
}

/// \brief Runs MatchingSubstack against the given list of stacks
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variadic template stuff here is neat, but we had some concerns about it because of the duplicated code in the Python wrapping. We'd really like to find a way to avoid that if possible.

Are there performance concerns or other reasons why this method couldn't accept something like a vector? I noticed PxrUsdMayaXformStack objects are non-copyable, but maybe we could relax that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, haven't done any benchmarking, but copying the entire stare object would end up copying multiple vectors, so I'd like to avoid it. I could make it take a vector of pointers, but then I'd either have to have it take raw pointers, or switch PxrUsdMayaXformStack to use either weak or ref ptrs.

I guess right now I'm leaning toward just making PxrUsdMayaXformStack derive from TfRefBase, switch everything to use refPtrs instead of refs, and then make FirstMatchingSubstack take a vector of those.

I'm also thinking I may just switch PxrUsdMayaXformOpClassification to use refPtrs, instead of weak ptrs... both for consistency with FirstMatchingSubstack, and because when I first chose weak ptrs, and because I've since realized that refPtrs are slightly lighter-weight (other than the initial copy from static memory to heap-allocated, but that will only be done once per stack). Again, will have to update everything to use refptrs... but will probably simplify things, and don't have to make any assumptions about how things are allocated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, after having found that you can't (or at least, it's difficult) to python-wrap const-ref-ptrs, I think I'm just going to make FirstMatchingSubstack take a vector of raw ptrs. It feels slightly yucky to use raw pointers, but it also feels like overkill to introduce weak pointers purely so they can be used with this function. Let me know if you'd prefer weak pointers, though (or any other approach), and I'll change it.

private:
PxrUsdMayaXformStack(
const OpClassList ops,
const std::vector<std::pair<size_t, size_t> > inversionTwins,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to pass a const-ref in here instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, yes, that should be a const-ref. Good catch.

from maya.api import OpenMaya as OM
from maya import standalone

from pxr import Usd, UsdMaya
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you change the UsdMaya import here to look like they do in other tests, like:
https://github.com/PixarAnimationStudios/USD/blob/496775539c324af7cb2ea12ddbb80720865b6cb2/third_party/maya/lib/usdMaya/testenv/testUsdMayaReferenceAssemblyEdits.py#L26-L31

This is unfortunately an internal build issue that affects the USD repository; we'd like to fix the issue so this won't be needed in the future, but in the meantime we'd appreciate the change here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, easy fix...

@@ -164,6 +166,7 @@ pxr_test_scripts(
testenv/testUsdMayaProxyShape.py
testenv/testUsdMayaReferenceAssemblyEdits.py
testenv/testUsdMayaUserExportedAttributes.py
testenv/testUsdMayaXformStack.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for adding a test!

@pmolodo
Copy link
Contributor Author

pmolodo commented Oct 13, 2017

Addressed the code review notes, as well as converted over both classes to be "value types", that store a refPtr to the actual data. Since both are immutable classes, this seemed a good compromise between safety / performance (ie, refPtr is lighter weight than weakPtr) / and ease-of-python-wrapping. Thanks to Alex Mohr for the suggestion!

Once again, this is the collapsed PR - the (new) incremental changesets can be seen here:

LumaPictures@728dfed
LumaPictures@711a547
LumaPictures@40ad81b
LumaPictures@c6db468
LumaPictures@5aa4fa8

@sunyab
Copy link
Contributor

sunyab commented Oct 17, 2017

Thanks! I've pulled these changes over again and am running them through builds now

@sunyab
Copy link
Contributor

sunyab commented Oct 20, 2017

Unfortunately this change is causing some internal test failures for usdExport. I'm looking at this further now, I will see if I can package up the test and send it your way to get your take on it.

@pmolodo
Copy link
Contributor Author

pmolodo commented Oct 25, 2017

Ok... so the test break is due to specifying rotateAxis using a single-axis (rotateX) xformOp. I had set rotateAxis to only accept rotateXYZ ops... because, unlike the "rotate" xform, it has no support for changing the rotation order (and, in fact, under the hood it's stored as a quaternion anyway!)... but it should at least be able to accept single-axis rotates, if not things like rotateZXY.

However... on thinking about it a bit more, I think this highlights one of the current shortcomings of my design - the maya-centric way in which it's handling rotation order. Granted, it's currently only used for Maya... but it was designed in a way that should be usable by other applications. For rotation order, though, it assumes that is set based off of the "rotate" op... and in fact, it sets a maya-only class (MTransformationMatrix::RotationOrder) to indicate what it what set to.

In a more general case, it's entirely possible for an xform stack to have 2 (or more) rotation ops, all of which may have their own independent rotation orders... so I think I'm going to get rid of handling of rotation-order as part of the API. Instead, the paradigm will be:

  • any 3-axis rotate in a stack is compatible with any-other 3-axis rotate (or 1-axis rotate)... since, at minimum, it is always possible to convert between them
  • it will be the responsibility of the user of the API to check the rotate-order of the matched xforms, and either set a rotate order in their app, or convert, as appropriate to their context
  • therefore, the optional RotationOrder* pointer will be dropped from the API for MatchingSubstack / FirstMatchingSubstack

I should have these changes (and some more tests) by EOD

@spiffmon
Copy link
Member

spiffmon commented Oct 25, 2017 via email

@pmolodo
Copy link
Contributor Author

pmolodo commented Oct 25, 2017

So, made the changes I mentioned above. rotateAxis can now handle single-axis rotations, as well as will automatically cross-convert from non-XYZ-ordered triple-axis rotates.

@spiffmon absolutely, triple-axis rotates are still recorded as such. I just meant that if you have a USD file that records it's rotateAxis as rotateYZX, this new version will automatically convert that to rotateXYZ on import, which is the only format that transform nodes will accept as input. It means that the maya stack is now slighly more tolerant (though an xformOp:rotateYZX:rotateAxis will not round-trip - ie, it will import fine, and maintain the same transformation / rotation, but when you re-ouput a new USD, it will be xformOp:rotateXYZ:rotateAxis).

@pmolodo
Copy link
Contributor Author

pmolodo commented Oct 25, 2017

Oh, also, in case you want to see incremental commits:

LumaPictures@9bb528b
LumaPictures@3215095

Copy link
Contributor

@sunyab sunyab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our sets team had a few more notes in review, mostly just cosmetic cleanup and documentation updates. There's one larger note about not using exceptions in the codebase, but this should be hopefully easily replaced with TF_VERIFY.

Thanks!

@@ -0,0 +1,1161 @@
#!/pxrpythonsubst
#
# Copyright 2016 Pixar
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2017 :)


PxrUsdMayaXformStack(
const OpClassList& ops,
const std::vector<std::pair<size_t, size_t> >& inversionTwins,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be const std::vector<IndexPair>& ...?

OpClassList const & GetOps() const;

PXRUSDMAYA_API
std::vector<std::pair<size_t, size_t> > const & GetInversionTwins() const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use const std::vector<IndexPair>& ... here as well?

/// \param opName the name of the operator classification we wish to find
/// \param isInvertedTwin the returned op classification object must match
/// this param for it's IsInvertedTwin() - if an op is found that matches
/// the name, but has the wrong invertedTwin status, nullptr is returned
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this should be "NO_INDEX is returned" instead of "nullptr is returned"

/// \param isInvertedTwin the returned op classification object must match
/// this param for it's IsInvertedTwin() - if an op is found that matches
/// the name, but has the wrong invertedTwin status, nullptr is returned
/// return Undex to the op classification object with the given name (and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor typo: "Undex" instead of "Index"


_PxrUsdMayaXformStackData(
const OpClassList& ops,
const std::vector<std::pair<size_t, size_t> >& inversionTwins,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const std::vector<IndexPair>& ... here?

return _sharedData->m_ops;
}

std::vector<std::pair<size_t, size_t> > const &
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const std::vector<IndexPair>& ... here?

// Insert, and check if it already existed
if (!result.insert({attrName, indexPair}).second)
{
// The attrName was already in the lookup map...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our coding convention is to not use exceptions in our code, so we'd prefer not to throw here. If this is something that should never happen (i.e., it's an invariant in the code and not reliant on user input), we prefer to use TF_VERIFY to indicate that something is wrong, like:

TF_VERIFY(result.insert({attrName, indexPair}).second, 
          "AttrName %s already found in attrName lookup map", 
          attrName.GetText());

This will cause the error output to be emitted if the condition is false, but allows the code to continue executing so that it doesn't cause the program to crash.

std::string msg = TfStringPrintf(
"Op classification name %s already found in op lookup map",
op.GetName().GetText());
throw std::invalid_argument(msg);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See note about exceptions and TF_VERIFY above

{
const PxrUsdMayaXformOpClassification& first = m_ops[pair.first];
const PxrUsdMayaXformOpClassification& second = m_ops[pair.second];
if (first.GetName() != second.GetName())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See note about exceptions and TF_VERIFY above

@pmolodo
Copy link
Contributor Author

pmolodo commented Nov 16, 2017

Ok, addressed the notes. Incremental commit can be found here:

LumaPictures@bf06845

Also made one other small tweak - marking the python wrapper for XformStack as noncopyable:

LumaPictures@cdac1b8

@pixar-oss pixar-oss merged commit 007e6c0 into PixarAnimationStudios:dev Nov 18, 2017
pixar-oss added a commit that referenced this pull request Nov 18, 2017
[maya] factor out matching of xforms into PxrUsdMayaXformStack
@sunyab
Copy link
Contributor

sunyab commented Nov 18, 2017

Just wanted to note I had added a follow-up commit ddd583f to fix a build issue on Windows. I also had to remove the noncopyable change to the XformStack's Python wrapping, as it caused the associated unit test to fail.

@pmolodo
Copy link
Contributor Author

pmolodo commented Nov 21, 2017

Doh - my mistake. Completely forgot that I had switched to the shared-data-ref-ptr paradigm, and that copies were now ok / preferred! In any case, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants