As a tools developoer, it is in my best interest to make the code run fast, and if there is anything in Maya that makes things go fast, it’s OpenMaya:
Below is an example of how OpenMaya iterates a scene to find all the AnimCurve nodes connected to an object in Maya.:
# import maya modules
from maya import OpenMaya as om
def get_connected_nodes(object_name="", find_node_type=om.MFn.kAnimCurve): """ get connected nodes from node provided. :param object_name: <str> string object to use for searching from. :param find_node_type: <om.MFn> kObjectName type to find. """ node = get_m_obj(object_name) dag_iter = om.MItDependencyGraph( node, om.MItDependencyGraph.kUpstream, om.MItDependencyGraph.kPlugLevel) dag_iter.reset() found_nodes = [] while not dag_iter.isDone(): cur_item = dag_iter.currentItem() if cur_item.hasFn(find_node_type): found_nodes.append(cur_item) dag_iter.next() return found_nodes
Another way to go about doing this business is creating a generator object by introducing yield, in the same code, we just remove the return statement:
# import maya modules
from maya import OpenMaya as om
def get_connected_nodes_gen(object_name="", find_node_type=om.MFn.kAnimCurve): """ nodes generator. :param object_name: <str> string object to use for searching from. :param find_node_type: <om.MFn> kObjectName type to find. """ node = get_m_obj(object_name) dag_iter = om.MItDependencyGraph( node, om.MItDependencyGraph.kUpstream, om.MItDependencyGraph.kPlugLevel) dag_iter.reset() while not dag_iter.isDone(): cur_item = dag_iter.currentItem() if cur_item.hasFn(find_node_type): yield cur_item dag_iter.next()
We can test the speed of the code by utilizing the cProfile module:
# import maya modules
from maya import OpenMaya as om
# import local modules
import cProfile
# define variables
anim_key_nodes = object_utils.get_connected_nodes_gen('pCube1')
# run profiler
cProfile.run("for n in anim_key_nodes: print n")
<...>
<...>
<maya.OpenMaya.MObject; proxy of <Swig Object of type 'MObject *' at 0x000002776FB30B40> >
<maya.OpenMaya.MObject; proxy of <Swig Object of type 'MObject *' at 0x000002776FB306C0> >
213 function calls in 0.021 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.020 0.020 0.021 0.021 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 OpenMaya.py:1539(__init__)
1 0.000 0.000 0.000 0.000 OpenMaya.py:7370(__init__)
14 0.000 0.000 0.000 0.000 OpenMaya.py:84(_swig_repr)
1 0.000 0.000 0.000 0.000 OpenMaya.py:9666(__init__)
15 0.000 0.000 0.001 0.000 object_utils.py:60(get_connected_nodes_gen)
1 0.000 0.000 0.000 0.000 object_utils.py:80(get_m_obj)
42 0.000 0.000 0.000 0.000 {maya._OpenMaya.MItDependencyGraph_currentItem}
43 0.000 0.000 0.000 0.000 {maya._OpenMaya.MItDependencyGraph_isDone}
42 0.000 0.000 0.000 0.000 {maya._OpenMaya.MItDependencyGraph_next}
1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MItDependencyGraph_reset}
1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MItDependencyGraph_swiginit}
42 0.000 0.000 0.000 0.000 {maya._OpenMaya.MObject_hasFn}
1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MObject_swiginit}
1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MSelectionList_add}
1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MSelectionList_getDependNode}
1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MSelectionList_swiginit}
1 0.000 0.000 0.000 0.000 {maya._OpenMaya.new_MItDependencyGraph}
1 0.000 0.000 0.000 0.000 {maya._OpenMaya.new_MObject}
1 0.000 0.000 0.000 0.000 {maya._OpenMaya.new_MSelectionList}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
And there we have it, we have covered OpenMaya iteration by traversing the node connections and finding the corresponding node (in this case the AnimCurve node), and a python generator to show the similarities between a loop and a generator. And yes, you can do the similar basic maya command, but it’s not as fun:
# import maya modules from maya import cmds def get_connected_anim(object_name=""): """ get connected nodes from node provided. :param object_name: <str> string object to use for searching from. :param find_node_type: <om.MFn> kObjectName type to find. """ anim_c = cmds.listConnections(object_name, s=1, d=0, type='animCurve') anim_b = cmds.listConnections(object_name, s=1, d=0, type='blendWeighted') anim_curves = [] if not anim_c and anim_b: for blend_node in anim_b: anim_curves.extend(cmds.listConnections(blend_node, s=1, d=0, type='animCurve')) return anim_curves else: return anim_c ... animCurveUL4 animCurveUL5 animCurveUL8 animCurveUL10 animCurveUL12 2 function calls in 0.013 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.013 0.013 0.013 0.013 :1() 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
Yes this seems like regular maya commands are faster, but you’ve got to remember, this is just a couple of nodes. What if you had to loop through a large set of vertices on a piece of geometry? I’ve created two functions: One loops through vertices by using standard maya cmds, the other using OpenMaya MItMeshVertex:
def get_mesh_points(object_name): """ Mesh points iterator. :param object_name: <str> object name. :return: <list> vertex positions """ mesh_fn, mesh_ob, mesh_dag = get_mesh_fn(object_name) mesh_it = om.MItMeshVertex(mesh_ob) mesh_vertexes = [] print("[Number of Vertices] :: {}".format(mesh_fn.numVertices())) while not mesh_it.isDone(): mesh_vertexes.append(mesh_it.position()) mesh_it.next() return mesh_vertexes def get_mesh_points_cmds(object_name): """ Mesh points iterator. :param object_name: <str> object name. :return: <list> vertex positions """ mesh_vertices = cmds.ls(object_name + '.vtx[*]', flatten=1) print("[Number of Vertices] :: {}".format(len(mesh_vertices))) nums = [] for i in mesh_vertices: nums.append(i) return nums
Now let’s see how cProfiler works on them both by iterating through 429510 vertices mesh:
# run cmdsiterator cProfile.run("object_utils.get_mesh_points_cmds('Emmanuel_Guevarra_Ian_McKellen_medres:Group2')") [Number of Vertices] :: 429510 429516 function calls in 1.629 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.019 0.019 1.629 1.629 :1() 1 0.038 0.038 1.609 1.609 object_utils.py:128(get_mesh_points_cmds) 1 1.548 1.548 1.548 1.548 {built-in method ls} 1 0.000 0.000 0.000 0.000 {len} 429510 0.023 0.000 0.023 0.000 {method 'append' of 'list' objects} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1 0.000 0.000 0.000 0.000 {method 'format' of 'str' objects}
# run OpenMaya iterator cProfile.run("object_utils.get_mesh_points('Emmanuel_Guevarra_Ian_McKellen_medres:Group2')") [Number of Vertices] :: 429510 1718065 function calls in 0.860 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.059 0.059 0.860 0.860 :1() 1 0.000 0.000 0.000 0.000 OpenMaya.py:2790(init) 1 0.000 0.000 0.000 0.000 OpenMaya.py:5304(init) 1 0.000 0.000 0.000 0.000 OpenMaya.py:7701(init) 1 0.000 0.000 0.000 0.000 OpenMaya.py:9666(init) 1 0.313 0.313 0.801 0.801 object_utils.py:112(get_mesh_points) 1 0.000 0.000 0.000 0.000 object_utils.py:142(get_mesh_fn) 1 0.000 0.000 0.000 0.000 {isinstance} 1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MDagPath_extendToShapeDirectlyBelow} 1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MDagPath_node} 1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MDagPath_swiginit} 1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MFnMesh_numVertices} 1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MFnMesh_swiginit} 429511 0.030 0.000 0.030 0.000 {maya._OpenMaya.MItMeshVertex_isDone} 429510 0.035 0.000 0.035 0.000 {maya._OpenMaya.MItMeshVertex_next} 429510 0.393 0.000 0.393 0.000 {maya._OpenMaya.MItMeshVertex_position} 1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MItMeshVertex_swiginit} 1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MObject_hasFn} 1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MSelectionList_add} 1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MSelectionList_getDagPath} 1 0.000 0.000 0.000 0.000 {maya._OpenMaya.MSelectionList_swiginit} 1 0.000 0.000 0.000 0.000 {maya._OpenMaya.new_MDagPath} 1 0.000 0.000 0.000 0.000 {maya._OpenMaya.new_MFnMesh} 1 0.000 0.000 0.000 0.000 {maya._OpenMaya.new_MItMeshVertex} 1 0.000 0.000 0.000 0.000 {maya._OpenMaya.new_MSelectionList} 429510 0.030 0.000 0.030 0.000 {method 'append' of 'list' objects} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1 0.000 0.000 0.000 0.000 {method 'format' of 'str' objects}
for cmds and OpenMaya, 1.629 seconds and 0.860 seconds respectively.