Skip to content

Commit

Permalink
[usdMaya] add filterTypes option to avoid exporting certain node types
Browse files Browse the repository at this point in the history
  • Loading branch information
pmolodo committed Apr 24, 2018
1 parent fdf9177 commit e28192d
Show file tree
Hide file tree
Showing 9 changed files with 584 additions and 1 deletion.
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 @@ -166,6 +166,7 @@ pxr_test_scripts(
testenv/testUsdExportCamera.py
testenv/testUsdExportColorSets.py
testenv/testUsdExportDisplayColor.py
testenv/testUsdExportFilterTypes.py
testenv/testUsdExportFrameOffset.py
testenv/testUsdExportInstances.py
testenv/testUsdExportLocator.py
Expand Down Expand Up @@ -280,6 +281,21 @@ pxr_register_test(testUsdExportDisplayColor
MAYA_APP_DIR=<PXR_TEST_DIR>/maya_profile
)

pxr_install_test_dir(
SRC testenv/UsdExportFilterTypesTest
DEST testUsdExportFilterTypes
)
pxr_register_test(testUsdExportFilterTypes
CUSTOM_PYTHON "${MAYA_BASE_DIR}/bin/mayapy"
COMMAND "${CMAKE_INSTALL_PREFIX}/tests/testUsdExportFilterTypes"
TESTENV testUsdExportFilterTypes
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/UsdExportFrameOffsetTest
DEST testUsdExportFrameOffset
Expand Down
44 changes: 44 additions & 0 deletions third_party/maya/lib/usdMaya/JobArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include "pxr/usd/usdGeom/tokens.h"

#include <maya/MDagPath.h>
#include <maya/MNodeClass.h>
#include <maya/MTypeId.h>

#include <ostream>
#include <string>
Expand Down Expand Up @@ -121,6 +123,10 @@ operator <<(std::ostream& out, const JobExportArgs& exportArgs)
for (const MDagPath& dagPath : exportArgs.dagPaths) {
out << " " << dagPath.fullPathName().asChar() << std::endl;
}
out << "filteredTypeIds (" << exportArgs.getFilteredTypeIds().size() << ")" << std::endl;
for (unsigned int id : exportArgs.getFilteredTypeIds()) {
out << " " << id << ": " << MNodeClass(MTypeId(id)).className() << std::endl;
}

out << "chaserNames (" << exportArgs.chaserNames.size() << ")" << std::endl;
for (const std::string& chaserName : exportArgs.chaserNames) {
Expand Down Expand Up @@ -151,6 +157,44 @@ void JobExportArgs::setParentScope(const std::string& ps) {
}
}

void JobExportArgs::addFilteredTypeName(const MString& typeName)
{
MNodeClass cls(typeName);
unsigned int id = cls.typeId().id();
if (id == 0) {
MGlobal::displayWarning(MString("Given excluded node type '") + typeName
+ "' does not exist; ignoring");
return;
}
filteredTypeIds.insert(id);
// We also insert all inherited types - only way to query this is through mel,
// which is slower, but this should be ok, as these queries are only done
// "up front" when the export starts, not per-node
MString queryCommand("nodeType -isTypeName -derived ");
queryCommand += typeName;
MStringArray inheritedTypes;
MStatus status = MGlobal::executeCommand(queryCommand, inheritedTypes, false, false);
if (!status) {
MGlobal::displayWarning(MString("Error querying derived types for '") + typeName
+ "': " + status.errorString());
return;
}

for (unsigned int i=0; i < inheritedTypes.length(); ++i) {
if (inheritedTypes[i].length() == 0) continue;
id = MNodeClass(inheritedTypes[i]).typeId().id();
if (id == 0) {
// Unfortunately, the returned list will often include weird garbage, like
// "THconstraint" for "constraint", which cannot be converted to a MNodeClass,
// so just ignore these...
// MGlobal::displayError(MString("Given inherited excluded node type '") + inheritedTypes[i]
// + "' does not exist; ignoring");
continue;
}
filteredTypeIds.insert(id);
}
}

JobImportArgs::JobImportArgs()
:
shadingMode(PxrUsdMayaShadingModeTokens->displayColor),
Expand Down
22 changes: 22 additions & 0 deletions third_party/maya/lib/usdMaya/JobArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@
#include "pxr/base/tf/token.h"
#include "pxr/usd/sdf/path.h"

#include <maya/MString.h>

#include <map>
#include <ostream>
#include <set>
#include <string>
#include <vector>

Expand Down Expand Up @@ -125,8 +128,27 @@ struct JobExportArgs
const SdfPath& getParentScope() const {
return parentScope;
}

PXRUSDMAYA_API
void addFilteredTypeName(const MString& typeName);

const std::set<unsigned int>& getFilteredTypeIds() const {
return filteredTypeIds;
}

void clearFilteredTypeIds() {
filteredTypeIds.clear();
}

private:
SdfPath parentScope;

// Maya type ids to avoid exporting; these are
// EXACT types, though the only exposed way to modify this,
// addFilteredTypeName, will also add all inherited types
// (so if you exclude "constraint", it will also exclude
// "parentConstraint")
std::set<unsigned int> filteredTypeIds;
};

PXRUSDMAYA_API
Expand Down

Large diffs are not rendered by default.

224 changes: 224 additions & 0 deletions third_party/maya/lib/usdMaya/testenv/testUsdExportFilterTypes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
#!/pxrpythonsubst
#
# Copyright 2018 Pixar
#
# Licensed under the Apache License, Version 2.0 (the "Apache License")
# with the following modification; you may not use this file except in
# compliance with the Apache License and the following modification to it:
# Section 6. Trademarks. is deleted and replaced with:
#
# 6. Trademarks. This License does not grant permission to use the trade
# names, trademarks, service marks, or product names of the Licensor
# and its affiliates, except as required to comply with Section 4(c) of
# the License and to reproduce the content of the NOTICE file.
#
# You may obtain a copy of the Apache License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the Apache License with the above modification is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the Apache License for the specific
# language governing permissions and limitations under the Apache License.
#

import os
import unittest

from maya import cmds
from maya import standalone

from pxr import Usd
from pxr import UsdGeom
from pxr import Vt
from pxr import Gf

class testUsdExportFilterTypes(unittest.TestCase):

@classmethod
def setUpClass(cls):
standalone.initialize('usd')

cmds.file(os.path.abspath('UsdExportFilterTypesTest.ma'), open=True,
force=True)

cmds.loadPlugin('pxrUsd', quiet=True)

@classmethod
def tearDownClass(cls):
standalone.uninitialize()

def doExportImportOneMethod(self, method, constraint=True, place3d=True,
nonExist=True, mergeXform=False):
name = 'UsdExportFilterTypes'
filter =[]
if not constraint:
name += '_noConstraint'
filter.append('constraint')
if not place3d:
name += '_noPlace3d'
filter.append('place3dTexture')
if not nonExist:
name += '_noNonExist'
filter.append('nonExist')
if method == 'cmd':
name += '.usda'
elif method == 'translator':
# the translator seems to be only able to export .usd - or at least,
# I couldn't find an option to do .usda instead...
name += '.usd'
else:
raise ArgumentError("unknown method: {}".format(method))
usdFile = os.path.abspath(name)

if method == 'cmd':
exportMethod = cmds.usdExport
args = ()
kwargs = {
'file': usdFile,
'mergeTransformAndShape': mergeXform,
'shadingMode': 'none',
}
if filter:
kwargs['filterTypes'] = tuple(filter)
else:
exportMethod = cmds.file
args = (usdFile,)
kwargs = {
'type': 'pxrUsdExport',
'exportAll': 1,
'f': 1,
}
options = {
'mergeXForm': int(mergeXform),
'shadingMode': 'None',
}
if filter:
options['filterTypes'] = ','.join(filter)
kwargs['options'] = ';'.join('{}={}'.format(key, val)
for key, val in options.iteritems())
print "running: {}(*{!r}, **{!r})".format(exportMethod.__name__, args, kwargs)
exportMethod(*args, **kwargs)
return Usd.Stage.Open(usdFile)

def doExportImportTest(self, validator, constraint=True, place3d=True,
mergeXform=False):
# test with all 4 combinations of nonExist and cmd/translator,
# since none should affect the result
stage = self.doExportImportOneMethod('cmd',
constraint=constraint,
place3d=place3d,
nonExist=True,
mergeXform=mergeXform)
validator(stage)
stage = self.doExportImportOneMethod('cmd',
constraint=constraint,
place3d=place3d,
nonExist=False,
mergeXform=mergeXform)
validator(stage)
stage = self.doExportImportOneMethod('translator',
constraint=constraint,
place3d=place3d,
nonExist=True,
mergeXform=mergeXform)
validator(stage)
stage = self.doExportImportOneMethod('translator',
constraint=constraint,
place3d=place3d,
nonExist=False,
mergeXform=mergeXform)
validator(stage)

def assertPrim(self, stage, path, type):
prim = stage.GetPrimAtPath(path)
self.assertTrue(prim.IsValid())
self.assertEqual(prim.GetTypeName(), type)

def assertNotPrim(self, stage, path):
self.assertFalse(stage.GetPrimAtPath(path).IsValid())

def testExportFilterTypes_noFilters_noMerge(self):
def validator(stage):
self.assertPrim(stage, '/pCube1', 'Xform')
self.assertPrim(stage, '/pCube1/pCubeShape1', 'Mesh')
self.assertPrim(stage, '/pCube1/place3dTexture1', 'Xform')
self.assertPrim(stage, '/pPyramid1', 'Xform')
self.assertPrim(stage, '/pPyramid1/pPyramidShape1', 'Mesh')
self.assertPrim(stage, '/pPyramid1/pPyramid1_parentConstraint1', 'Xform')
self.doExportImportTest(validator)

def testExportFilterTypes_noConstraint_noMerge(self):
def validator(stage):
self.assertPrim(stage, '/pCube1', 'Xform')
self.assertPrim(stage, '/pCube1/pCubeShape1', 'Mesh')
self.assertPrim(stage, '/pCube1/place3dTexture1', 'Xform')
self.assertPrim(stage, '/pPyramid1', 'Xform')
self.assertPrim(stage, '/pPyramid1/pPyramidShape1', 'Mesh')
self.assertNotPrim(stage, '/pPyramid1/pPyramid1_parentConstraint1')
self.doExportImportTest(validator, constraint=False)

def testExportFilterTypes_noPlace3d_noMerge(self):
def validator(stage):
self.assertPrim(stage, '/pCube1', 'Xform')
self.assertPrim(stage, '/pCube1/pCubeShape1', 'Mesh')
self.assertNotPrim(stage, '/pCube1/place3dTexture1')
self.assertPrim(stage, '/pPyramid1', 'Xform')
self.assertPrim(stage, '/pPyramid1/pPyramidShape1', 'Mesh')
self.assertPrim(stage, '/pPyramid1/pPyramid1_parentConstraint1', 'Xform')
self.doExportImportTest(validator, place3d=False)

def testExportFilterTypes_noConstraintPlace3d_noMerge(self):
def validator(stage):
self.assertPrim(stage, '/pCube1', 'Xform')
self.assertPrim(stage, '/pCube1/pCubeShape1', 'Mesh')
self.assertNotPrim(stage, '/pCube1/place3dTexture1')
self.assertPrim(stage, '/pPyramid1', 'Xform')
self.assertPrim(stage, '/pPyramid1/pPyramidShape1', 'Mesh')
self.assertNotPrim(stage, '/pPyramid1/pPyramid1_parentConstraint1')
self.doExportImportTest(validator, constraint=False, place3d=False)

def testExportFilterTypes_noFilters_merge(self):
def validator(stage):
self.assertPrim(stage, '/pCube1', 'Xform')
self.assertPrim(stage, '/pCube1/pCubeShape1', 'Mesh')
self.assertPrim(stage, '/pCube1/place3dTexture1', 'Xform')
self.assertPrim(stage, '/pPyramid1', 'Xform')
self.assertPrim(stage, '/pPyramid1/pPyramidShape1', 'Mesh')
self.assertPrim(stage, '/pPyramid1/pPyramid1_parentConstraint1', 'Xform')
self.doExportImportTest(validator, mergeXform=True)

def testExportFilterTypes_noConstraint_merge(self):
def validator(stage):
self.assertPrim(stage, '/pCube1', 'Xform')
self.assertPrim(stage, '/pCube1/pCubeShape1', 'Mesh')
self.assertPrim(stage, '/pCube1/place3dTexture1', 'Xform')
self.assertPrim(stage, '/pPyramid1', 'Mesh')
self.assertNotPrim(stage, '/pPyramid1/pPyramidShape1')
self.assertNotPrim(stage, '/pPyramid1/pPyramid1_parentConstraint1')
self.doExportImportTest(validator, constraint=False, mergeXform=True)

def testExportFilterTypes_noPlace3d_merge(self):
def validator(stage):
self.assertPrim(stage, '/pCube1', 'Mesh')
self.assertNotPrim(stage, '/pCube1/pCubeShape1')
self.assertNotPrim(stage, '/pCube1/place3dTexture1')
self.assertPrim(stage, '/pPyramid1', 'Xform')
self.assertPrim(stage, '/pPyramid1/pPyramidShape1', 'Mesh')
self.assertPrim(stage, '/pPyramid1/pPyramid1_parentConstraint1', 'Xform')
self.doExportImportTest(validator, place3d=False, mergeXform=True)

def testExportFilterTypes_noConstraintPlace3d_merge(self):
def validator(stage):
self.assertPrim(stage, '/pCube1', 'Mesh')
self.assertNotPrim(stage, '/pCube1/pCubeShape1')
self.assertNotPrim(stage, '/pCube1/place3dTexture1')
self.assertPrim(stage, '/pPyramid1', 'Mesh')
self.assertNotPrim(stage, '/pPyramid1/pPyramidShape1')
self.assertNotPrim(stage, '/pPyramid1/pPyramid1_parentConstraint1')
self.doExportImportTest(validator, constraint=False, place3d=False, mergeXform=True)


if __name__ == '__main__':
unittest.main(verbosity=2)
8 changes: 8 additions & 0 deletions third_party/maya/lib/usdMaya/usdExport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ MSyntax usdExport::createSyntax()
syntax.makeFlagMultiUse("-frameSample");

syntax.addFlag("-ro" , "-renderableOnly", MSyntax::kNoArg);
syntax.addFlag("-ft" , "-filterTypes", MSyntax::kString);
syntax.makeFlagMultiUse("-filterTypes");
syntax.addFlag("-sl" , "-selection", MSyntax::kNoArg);
syntax.addFlag("-dc" , "-defaultCameras", MSyntax::kNoArg);
syntax.addFlag("-rlm" , "-renderLayerMode" , MSyntax::kString);
Expand Down Expand Up @@ -332,6 +334,12 @@ try
}

jobArgs.excludeInvisible = argData.isFlagSet("renderableOnly");
unsigned int numFilteredTypes = argData.numberOfFlagUses("filterTypes");
for (unsigned int i=0; i < numFilteredTypes; i++) {
MArgList tmpArgList;
argData.getFlagArgumentList("filterTypes", i, tmpArgList);
jobArgs.addFilteredTypeName(tmpArgList.asString(0));
}
jobArgs.exportDefaultCameras = argData.isFlagSet("defaultCameras");

if (argData.isFlagSet("renderLayerMode")) {
Expand Down
Loading

0 comments on commit e28192d

Please sign in to comment.