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.