::Winning The Perf War::

July 21st, 2010 by hamish download the zooToolBox

So one super convenient thing pymel gives you is a name independent way of tracking nodes in your code. So doing this:

from maya.cmds import *
o = group( em=True )
rename( o, 'something_else' )
select( o )

This will fail because the o variable is just the name of the group when it was originally created, and the name changes before the select command is run. Now obviously the above is a trivial case and can be easily solved, but sometimes its not so easy.

For example, if you have a group with a non-unique leaf name that is nested deeply in the hierarchy, just by re-parenting one of its parents, the node will change. Tracking this changed name is non-trivial, and is the sort of problem you run into every now and then when writing generalized tools for rigging, or just general scene construction.

Anyhoo, so poking around a bit I experimented with adding a few methods to MObject, kinda like this:

from maya.OpenMaya import *
def asMObject( otherMobject=None ):
	'''
	tries to cast the given obj to an mobject - it can be string
	'''
	if otherMobject is None:
		return MObject()

	if isinstance( otherMobject, basestring ):
		sel = MSelectionList()
		sel.add( otherMobject )

		tmp = MObject()
		sel.getDependNode( 0, tmp )
		#tmp.cast()

		return tmp

def partialPathName( self ):
	'''
	returns the partial name of the node
	'''
	if self.hasFn( MFn.kDagNode ):
		dagPath = MDagPath()
		MDagPath.getAPathTo( self, dagPath )

		return dagPath.partialPathName()

	return MFnDependencyNode( self ).name()

MObject.__str__ = partialPathName
MObject.__unicode__ = partialPathName
MObject.partialPathName = partialPathName
MObject.name = partialPathName

def _hash( self ):
	return MObjectHandle( self ).hashCode()

MObject.__hash__ = _hash

def cmpNodes( a, b ):
	'''
	compares two nodes and returns whether they're equivalent or not.
	the compare is done on MObjects not strings so passing the fullname
	and a partial name to the same dag will still return True
	'''
	if not isinstance( a, MObject ):
		a = asMObject( a )

	if not isinstance( b, MObject ):
		b = asMObject( b )

	return a.name() == b.name()

Now this is hardly rocket science here, but it does go along way toward making MObjects useful in just your average script, and more to the point it solves the problem discussed above.

How? Well, first off, its easy to cast a node name to an MObject with the asMObject function. More importantly, you can still pass the variable to a normal mel command. ie this works:

from maya.cmds import *
o = asMObject( group( em=True ) )
rename( o, 'something_else' )
select( o )

Plus you can compare two nodes without having to worry about whether one is a full path to the node or a partial path.

This code can be packaged up into a simple little module that you can import at will – simply by importing the script you get the functionality added to the MObject class, so all you need to do really is add this to the top of your scripts:

from apiExtensions import asMObject

And wham – you get the rest for free.

Casting to these MObjects is still slow – there’s no way around that. Thats just maya being a slut. But you can easily target which parts of your script to add this functionality to without having to change your entire code base to use something like pymel or MRV. Plus you can also pass this MObject to the api class – again without having to wrap anything.

I’m smelling win.

Thoughts?

Share

This post is public domain

This entry was posted on Wednesday, July 21st, 2010 at 15:17 and is filed under main. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

  • Byron

    As you are working with MObjects only in your asMObject function, you don’t handle instances correctly. That can possibly be fixed though by monkey-patching the MDagPath type as well.

    Considering that your effort is set out to improve performance over PyMel, I can assure you that you don’t win more than a more stable handle to objects in the scene. This can be beneficial in some cases, but its being payed for with the expensive conversion to and from MObjects.

    To me, converting an MObject or MDagPath to strings to use MEL already is a waste of cycles, as the same thing can usually be done using the maya API, without forcing you to do any object conversions ( yes, lets set any discussion about MScriptUtil aside for now ;) ).
    Staying within the python-maya API at all times can be faster than the respective ( and well-written ) MEL code if done correctly.

    Not using PyMel because of its performance issues is a comprehensible, but not using any framework because of performance concerns might not only reduce your productivity, but also the maintainability of the resulting code.

  • hamish

    thanks byron – I haven;t worked with instances for a loooong time, so they’re not even part of my thinking. but thanks for the heads up, thats really helpful.

    I’m not setting out to improve pymel really – I just want a lightweight way of leveraging the api for the times that I need to.

    And you’re right, casting to MObjects is definitely a waste of cycles (why autodesk?!), so being able to opt into doing that is really helpful. I wonder whether its possible to get an MObject by poking the maya api from python using ctypes? Anyone tried this?

    I need to look closer at your framework Byron – it sounds interesting. I have had a quick play around, but nothing comprehensive. I’m not sure I agree about the productivity thing though. In fact learning a new framework can make certain tools inaccessible to those who are more casual scripters. Its a hard decision to make properly. At least for someone who didn’t write the framework. ;)

  • Byron

    Personally I don’t believe that trying to handle the Maya API through ctypes, if it is even possible, will be faster than the native wrap autodesk provides. Afaik there is no other way of retrieving MObjects/DagPaths from a string other than using MSelectionLists.

    The problem here is that you either live with the imposed inefficiencies, or make the move to the API. Its still possible to call MEL of course, and obtaining a string name from an API object is lighting fast as well.

    You are right, thinking about moving to a new framework, especially one that embraces the Maya API as much as MRV does, is a big leap. The beginning will be hard as you don’t only have to learn the ways of the MRV framework, but additionally some of the ideas of the maya API. To me MRV feels more natural than the Maya API itself as it is much more ‘direct’ in the way you make the calls, which in turn improves your productivity and the readability of your code … compared to native Maya API code of course. MRV is definitely more verbose than PyMel is.

    Your concern appears to go further of course, how can you write software using MRV, without being sure its installed on any machine you want to run on ? The great thing is that, as a Development Framework, it comes equipped with tools that handle the packaging and distribution for you. Hence its easy to include a copy of your respective version of MRV within your toolset, the user doesn’t care or know about it.
    As a development framework, it also tries to help you to write reliable code, there are quite a few pages in the docs about the proposed workflow.

    But be warned, once you got the idea of it, you won’t want to go back :P.

  • hamish

    I think we’re solving different problems. For the most part mel does everything I need. Occasionally I will leverage the API when optimizing code, but for the most part it doesn’t add many capabilities. This is of course, just a rule of thumb – obviously there are times when this isn’t true, but we’re talking generalities here. So given that, my efforts have been about trying to minimize the pain of “crossing the mel/api boundary”. By default this is an awkward boundary to cross. Pymel manages it reasonably well, but the cost of this convenience is HUGE.

    Based on your comments it seems MRV is more about applying some sense to the API than minimizing the pain of leveraging the API from mel. Is that accurate?

    I work with a team of other technical artists, and we all write tools to solve workflow problems. Most of these tools can be done with mel, with the occasional call to the API, so there is little point having us learn a new framework to do what we’ve always been able to do. The problem I’m solving is making the times we need to use the API easier.

    Anyway, I still plan to look into MRV a lot more – if nothing else I’m quite sure I’ll learn a lot from your code.

  • Byron

    Without wanting to copy-paste text, and without wanting to become too inaccurate, I think its best to provide a link to the docs to explain what MRV is and what it is not: http://packages.python.org/MRV/about.html

    To me its very interesting to hear that you solve most of the tasks at hand by writing MEL code. Personally I found MEL to be a very restrictive programming environment, which is why I embraced python when it became available in maya. Now that you appear unwilling to pay the performance-costs of pymel, will you instead write python code using your own specialized helpers, or write code in MEL instead ? The reason for the initial creation of MRV was exactly that I didn’t like any of the available choices. The API seemed the most powerful and fastest, but it is too expensive to code everything in C++. MRV reduces the programming cost of using the API considerably from within python, making it usable for daily scripting and small or large tools of all kinds.

    Lets see what your experiences will be once you look into MRV, I’d be looking forward to reading about them here.

  • hamish

    when I say “mel” I mean maya’s python bindings to the mel commands. ie mel == maya.cmds

    MEL as a language is indeed restrictive – python makes a lot of things possible. The point of an abstraction layer is for the sake of unification.

    Anyway, I’ll keep you posted about what I discover with MRV. I have no doubt I’ll learn a heap – MRV looks like an impressive project (as is pymel). But I can’t imagine I’ll pay the cost of deploying it within the studio.