-
Notifications
You must be signed in to change notification settings - Fork 202
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-99006 Filter out objects to export by hierarchy. #657
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,7 +49,6 @@ | |
#include <maya/MPlug.h> | ||
#include <maya/MPlugArray.h> | ||
#include <maya/MPoint.h> | ||
#include <maya/MSelectionList.h> | ||
#include <maya/MStatus.h> | ||
#include <maya/MString.h> | ||
#include <maya/MStringArray.h> | ||
|
@@ -75,6 +74,39 @@ | |
|
||
PXR_NAMESPACE_USING_DIRECTIVE | ||
|
||
namespace { | ||
bool shouldAddToSet(const MDagPath& toAdd, const UsdMayaUtil::MDagPathSet& dagPaths) | ||
// Utility function to check if an object should be added to the set of objects to | ||
// export. An object should not be added if it's invalid, or if any of it's parent | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "it's" --> "its" |
||
// objects are already in the set. | ||
{ | ||
if (!toAdd.isValid()) | ||
return false; | ||
|
||
MStatus status = MS::kSuccess; | ||
bool pathIsValid = true; | ||
MDagPath dp = toAdd; | ||
|
||
UsdMayaUtil::MDagPathSet::const_iterator end = dagPaths.end(); | ||
|
||
// Travel up the hierarchy looking for a parent object that is already in the set. | ||
// That is our only reason to return false. Not finding any ancestors in the set | ||
// will eventually hit the world root, which will be an invalid path and in that case | ||
// we just exit the loop and return true. | ||
while (pathIsValid && status == MS::kSuccess) | ||
{ | ||
UsdMayaUtil::MDagPathSet::const_iterator dpIter = dagPaths.find(dp); | ||
if (dpIter != end) | ||
return false; | ||
|
||
status = dp.pop(); | ||
pathIsValid = dp.isValid(); | ||
} | ||
|
||
return true; | ||
} | ||
} | ||
|
||
double | ||
UsdMayaUtil::ConvertMDistanceUnitToUsdGeomLinearUnit( | ||
const MDistance::Unit mdistanceUnit) | ||
|
@@ -2101,3 +2133,48 @@ UsdMayaUtil::nameToDagPath(const std::string& name) | |
CHECK_MSTATUS(status); | ||
return dag; | ||
} | ||
|
||
void | ||
UsdMayaUtil::GetFilteredSelectionToExport(bool exportSelected, MSelectionList& objectList, UsdMayaUtil::MDagPathSet& dagPaths) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Had hoped that there was an easy API call or Maya command to do what I want here but I never found anything. Most of the code I found that did something similar ended up traversing the entire Maya scene using various dag iterators, sometimes multiple times, and it didn’t fit what we want to do here very well. What I ended up doing was:
|
||
{ | ||
dagPaths.clear(); | ||
|
||
bool filterInput = true; | ||
|
||
// There are three cases depending on the input: | ||
// If exportSelected is true then we will grab the active selection | ||
// If objectList is empty then we will grab all immediate children of the world root. | ||
// Else there was a populated list of objects to use, most likely passed explicitly to the command. | ||
if (exportSelected) { | ||
MGlobal::getActiveSelectionList(objectList); | ||
} else if (objectList.isEmpty()) { | ||
objectList.add("|*", true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I hadn't noticed this before, but will this miss any top-level objects that have a namespace? Is there a syntax that would catch objects with arbitrarily deep namespaces? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |* will catch all top-level objects, including objects in a namespace. I could add a test for something like this though, can you give an example of what you mean by arbitrarily deep namespaces? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, sorry, nevermind this then. Apparently selection lists work differently from the I did this in the script editor to setup a test scene: cmds.polyCube(name='Cube')
cmds.namespace(addNamespace=':NS_One')
cmds.namespace(setNamespace=':NS_One')
cmds.polyCube(name='Cube')
cmds.namespace(addNamespace=':NS_One:NS_Two')
cmds.namespace(setNamespace=':NS_One:NS_Two')
cmds.polyCube(name='Cube')
cmds.namespace(addNamespace=':NS_One:NS_Two:NS_Three')
cmds.namespace(setNamespace=':NS_One:NS_Two:NS_Three')
cmds.polyCube(name='Cube') That yields four top-level nodes: [n for n in cmds.ls(long=True) if n.endswith('Cube')]
# Result: [u'|Cube', u'|NS_One:Cube', u'|NS_One:NS_Two:Cube', u'|NS_One:NS_Two:NS_Three:Cube'] # If I try to find those nodes using cmds.ls('|*')
# Result: [u'Cube', u'front', u'persp', u'side', u'top'] #
cmds.ls('|*:*')
# Result: [u'NS_One:Cube'] #
cmds.ls('|*:*:*')
# Result: [u'NS_One:NS_Two:Cube'] #
cmds.ls('|*:*:*:*')
# Result: [u'NS_One:NS_Two:NS_Three:Cube'] # But the OpenMaya API indeed seems to behave the way you're describing: from maya.api import OpenMaya as OM
objectList = OM.MSelectionList()
objectList.add('|*', True)
for i in range(objectList.length()):
print(objectList.getDagPath(i)) That yields (ignore the default cameras):
|
||
// By construction, the list will only include the single top level objects | ||
// when we get the input list with |*, so no need to filter selection. | ||
filterInput = false; | ||
} | ||
|
||
unsigned int nbObj = objectList.length(); | ||
if (0 == nbObj) { | ||
return; | ||
} | ||
|
||
MStatus status; | ||
|
||
// Easiest way to filter by hierarchy is to: | ||
// 1. Put the input into a set that is sorted by distance from the world root. | ||
// 2. For each input object we iterate up its hierarchy checking if any parent is in the set. | ||
// 3. If no parent is in the set then we can add it. | ||
UsdMayaUtil::MDagPathSet sortedInput; | ||
for (unsigned int i=0; i < nbObj; i++) { | ||
MDagPath dagPath; | ||
status = objectList.getDagPath(i, dagPath); | ||
if (status == MS::kSuccess) | ||
sortedInput.emplace(dagPath); | ||
} | ||
|
||
for(auto pIter : sortedInput) { | ||
if (!filterInput || shouldAddToSet(pIter, dagPaths)) | ||
dagPaths.emplace(pIter); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,14 +29,14 @@ | |
#include <maya/MDataHandle.h> | ||
#include <maya/MDistance.h> | ||
#include <maya/MFnDagNode.h> | ||
#include <maya/MSelectionList.h> | ||
#include <maya/MFnDependencyNode.h> | ||
#include <maya/MFnMesh.h> | ||
#include <maya/MFnNumericData.h> | ||
#include <maya/MMatrix.h> | ||
#include <maya/MObject.h> | ||
#include <maya/MObjectHandle.h> | ||
#include <maya/MPlug.h> | ||
#include <maya/MSelectionList.h> | ||
#include <maya/MStatus.h> | ||
#include <maya/MString.h> | ||
|
||
|
@@ -66,7 +66,9 @@ struct _CmpDag | |
{ | ||
bool operator()(const MDagPath& lhs, const MDagPath& rhs) const | ||
{ | ||
return strcmp(lhs.fullPathName().asChar(), rhs.fullPathName().asChar()) < 0; | ||
int pathCountDiff = lhs.pathCount() - rhs.pathCount(); | ||
return (0 != pathCountDiff) ? (pathCountDiff < 0) : | ||
(strcmp(lhs.fullPathName().asChar(), rhs.fullPathName().asChar()) < 0); | ||
Comment on lines
-69
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changing the sorting function to more explicitly sort by hierarchy depth, and then alphabetically within the same depth. End result is the same because of the path separator used in the fullPathName, but adding the pathCount check makes it more obvious that it’s doing this. It would also be faster this way since the path count is a stored value internally so it’s a quick int compare in a lot of cases, avoiding a bunch of string compares. |
||
} | ||
}; | ||
|
||
|
@@ -595,6 +597,21 @@ bool containsUnauthoredValues(const VtIntArray& indices); | |
MAYAUSD_CORE_PUBLIC | ||
MDagPath nameToDagPath(const std::string& name); | ||
|
||
/// Utility function used by the export translator and commands to filter out objects to export | ||
/// by hierarchy. When an object is exported then all of its children are exported as well, so | ||
/// the children should be removed from the list before the set of objects ends up in the write | ||
/// job, where it would error out. | ||
/// If \p exportSelected is true then the active selection list will be added to \p objectList | ||
/// and then used to fill \p dagPaths with the objects to be exported. | ||
/// If \p exportSelected is false and \p objectList is not empty then \p objectList will | ||
/// be used to fill \p dagPaths with the objects to be exported. | ||
/// If \p exportSelected is false and \p objectList is empty then all objects starting at | ||
/// the DAG root will be added to \p objectList and then used to fill \p dagPaths with | ||
/// the objects to be exported. | ||
/// | ||
MAYAUSD_CORE_PUBLIC | ||
void GetFilteredSelectionToExport(bool exportSelected, MSelectionList& objectList, UsdMayaUtil::MDagPathSet& dagPaths); | ||
|
||
} // namespace UsdMayaUtil | ||
|
||
PXR_NAMESPACE_CLOSE_SCOPE | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,6 +52,12 @@ UsdMayaExportTranslator::writer(const MFileObject &file, | |
const MString &optionsString, | ||
MPxFileTranslator::FileAccessMode mode ) { | ||
|
||
// If we are in neither of these modes then there won't be anything to do | ||
if (mode != MPxFileTranslator::kExportActiveAccessMode && | ||
mode != MPxFileTranslator::kExportAccessMode) { | ||
return MS::kSuccess; | ||
} | ||
Comment on lines
+55
to
+59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic farther down used to be that we would only fill the input list if one of these two flags were set. If neither were set then we had nothing to export and just returned kSuccess. Moving this check to the start since there is no point in continuing if we are in a different mode. |
||
|
||
std::string fileName(file.fullName().asChar(), file.fullName().length()); | ||
VtDictionary userArgs; | ||
bool exportAnimation = false; | ||
|
@@ -123,24 +129,9 @@ UsdMayaExportTranslator::writer(const MFileObject &file, | |
} | ||
|
||
MSelectionList objSelList; | ||
if(mode == MPxFileTranslator::kExportActiveAccessMode) { | ||
// Get selected objects | ||
MGlobal::getActiveSelectionList(objSelList); | ||
} else if(mode == MPxFileTranslator::kExportAccessMode) { | ||
// Get all objects at DAG root | ||
objSelList.add("|*", true); | ||
} | ||
|
||
// Convert selection list to jobArgs dagPaths | ||
UsdMayaUtil::MDagPathSet dagPaths; | ||
unsigned int len = objSelList.length(); | ||
for (unsigned int i=0; i < len; i++) { | ||
MDagPath dagPath; | ||
if (objSelList.getDagPath(i, dagPath) == MS::kSuccess) { | ||
dagPaths.insert(dagPath); | ||
} | ||
} | ||
|
||
GetFilteredSelectionToExport((mode == MPxFileTranslator::kExportActiveAccessMode), objSelList, dagPaths); | ||
|
||
if (dagPaths.empty()) { | ||
TF_WARN("No DAG nodes to export. Skipping."); | ||
return MS::kSuccess; | ||
|
@@ -151,7 +142,7 @@ UsdMayaExportTranslator::writer(const MFileObject &file, | |
PXR_NS::UsdMayaJobExportArgs jobArgs = PXR_NS::UsdMayaJobExportArgs::CreateFromDictionary( | ||
userArgs, dagPaths, timeSamples); | ||
|
||
len = filteredTypes.length(); | ||
unsigned int len = filteredTypes.length(); | ||
for (unsigned int i=0; i < len; ++i) { | ||
jobArgs.AddFilteredTypeName(filteredTypes[i].asChar()); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We still use
MSelectionList
in this file below as a param toGetFilteredSelectionToExport()
?