setKeyFrame command equivalent in openMaya


#1

In terms of performance/speed, I’m wondering if there’s any merit in trying to replicate the setKeyFrame command in OpenMaya. For example, say I have 10000 curves and I want to insert a keyframe on each curve on frame 50. I would normally do this with the command:

cmds.setKeyframe(anim_curves, time=[50,50] )

This takes close to a second to compute. Could doing it in openMaya perform any better? I’ve tried to start it, but my openMaya is shabby currently:

import maya.OpenMaya as om
import maya.OpenMayaAnim as oma

for anim_curve in anim_curves:
    sel = om.MSelectionList()
    sel.add(anim_curve)
    mObj = om.MObject()
    sel.getDependNode(0, mObj)
    mCurve = oma.MFnAnimCurve(mObj)
    mCurve.addKey(om.MTime(50), oma.MFnAnimCurve.kTangentAuto, oma.MFnAnimCurve.kTangentAuto, False, None)

This kind of works, however it’s setting the keyframe at some arbitrary value (11), rather then preserving the shape of each curve. And it also seems to be taking longer than the python command. Am I going about this wrong? Thanks for any advice.


#2

I doubt that you will any speed gain if any at all.
The cmds module does the same as the mel commands, they call a command which is mostly coded in c++ what means that is is already as fast as possible.
What you do with the OpenMaya python api is to do the same c++ command in python what will be much slower I fear.


#3

Thanks for the feedback, I kind of assumed that may be the case. So my next question is when do we actually see the advantage of using openMaya over python/mell commands?

And just for the sake of improving my openMaya knowledge, how would I change my openMaya code above to actually preserve the shape of the curve? I can’t seem to figure it out…


#4

If you simply execute commands, there is no advantage.
But with OpenMaya, you can create your own nodes, plugins etc.
OpenMaya can be used to do things which cannot be simply done with cmds or if you have to use a lot cmds to do something.

e.g. you can have your own node to collect informations about the scene. This can be done by simply creating a locator or group node and add attributes. But it is much more convenient to have your own node type like “sceneData” and you can do a “ls -type sceneData”. And you can trigger actions as soon as a parameter changes.


#5

So that’s not entirely correct. There is a cost to invoking MEL even if the underlying command is in C++. Everything in MEL is all done in strings so there is string conversion overhead with MEL on top of the overhead of interfacing with the CommandEngine.

That said, it’s not always cut and dry because there is an overhead to Python as well. If you were to write this entirely in the C++ API I’d imagine the gains would be larger.

I’ve written up a short test that demonstrates the difference in this example.

import maya.cmds as cmds
import maya.api.OpenMaya as om
import maya.api.OpenMayaAnim as oma
import time

NUM_CURVES = 10000
TIME = 50
VALUE = 10

def cmd_setup(count):
    """
    Create count animCurves
    """
    cmds.file(f=True, new=True)
    nodes = []
    for i in range( count ):
        nodes.append( cmds.createNode('animCurveTU') )
    return nodes

def cmd_test():
    curves = cmd_setup( NUM_CURVES )
    start = time.time()
    cmds.setKeyframe( curves, time=TIME, value=VALUE )
    elapsed = time.time() - start
    print 'cmd_test took %ss' % elapsed
    
def om_setup(count):
    """
    Create count animCurves
    """
    dg_mod = om.MDGModifier()
    objs = om.MObjectArray()
    for i in range(count):
        objs.append( dg_mod.createNode( 'animCurveTU' ) )
    dg_mod.doIt()
    return objs
    
def om_test():
    objs = om_setup( NUM_CURVES )
    start = time.time()
    curve_fn = oma.MFnAnimCurve()
    for obj in objs:
        curve_fn.setObject( obj )
        curve_fn.addKey( om.MTime(TIME), VALUE )
    elapsed = time.time() - start
    sel_list = om.MSelectionList()
    for obj in objs:
        sel_list.add(obj)
    om.MGlobal.setActiveSelectionList( sel_list )
    print 'om_test took %ss' % elapsed
    
cmd_test()
om_test()

From my testing you get roughly the following results on my machine:

cmd_test took 0.192999839783s
om_test took 0.129999876022s

So om_test is faster. I think the parts that are taking longer in your original example Neilman is the selection list operations in OpenMaya. That part is slow. If you were to move the timers to include the extra utility part that I added (for debugging) where I add to the selection list and then select, cmd_test comes out ahead.

In any case, play around with the above code and you’ll see that the code that simply adds keyframes is faster in OM. It’s definitely a lot more code so I’m not entirely sure if the benefit is there.

Hope this helps.


#6

Interesting test, thanks for letting us know. Seems I have to revise my opinion.


#7

Thanks Keilun that’s really helpful to know. I need every millisecond I can get so I think this is still worth pursuing.


#8

Keep in mind it’s not too uncommon for other commands to not accept a list of objects to operate on as an argument. Rather you would have to write a loop and run the command repeatedly. It’s mostly those cases where the MEL overhead rears its head and OpenMaya dominates performance wise.

In this case, if you factor in all the extra code with selection lists, it’s a wash or probably slower and cmd_test might be the faster solution. Whether or not it will be faster in the end depends on how you plan on retrieving your curves. In om_test the MObjectArray was already returned by the setup function. If your implementation can already provide you with that info, you may save time going OpenMaya. If not, and you need to convert strings to MObjects via MSelectionList, then maya.cmds is likely faster.


#9

I have had good performance experiences when I collected setkeyframe commands in one string and executed it in one go via the eval command.