::check it out – video tutorials!::

February 4th, 2010 by hamish download the zooToolBox

super cool – the rigging mentor dudes have made a video tutorial for zooPickwalk!  its way cool – way cooler than anything I would have ever done.  so head on over and check it out.

::rigging mentor is a go::

February 3rd, 2010 by hamish download the zooToolBox

so the Rigging Mentor guys have officially launched their service.  wish em luck (good luck!), and if you’re trying to get into da biz, check em out, see if they work for you.

::fast skin loading!::

January 29th, 2010 by hamish download the zooToolBox

I finally spent a bit of time re-implementing my skin weight loading script in the api – more specifically, the python api bindings.  why?  well mel is super slow when working with skinning and I figured doing it in the api might help relieve the problem.

some numbers for you.  for a 3000 vert character its roughly 21 seconds using maya.cmds.  if you tune the numbers you can get that down to 12 seconds, but lets assume worst case scenarios here which I’ll describe in a bit more detail soon.  so using the api to do vertex iteration and skin weight assignment takes that down to 8 seconds.  thats a speed up of 2.4 times – which is not huge, but nothing to be scoffed at either.  lets looks at some details.

first up, the searching is being done entirely in python.  so given a vertex, i need to search through a cloud of 3000 points to find the closest matches in python.  the best case scenario above (12 seconds) uses a tiny search radius, which is useful for restoring weighting with minimal mesh changes.  the worst case scenario is for restoring weighting when the mesh has changed drastically – which happens, especially early on.  so the worst case scenario the search radius grows until it finds some matches, and then it averages the weighting found for the found verts and applies the weighting.  so it is this worst case scenario which is the most interesting.

so how does the searching work?  well basically it uses a binary search algorithm on the point cloud.  what does this mean?  i didn’t really know what a binary search was either until recently when a programmer friend of mine told me.  its pretty simple: basically given a sorted list of values, you look at the middle value and figure out whether the value you want to match is greater than or less than that value and throw away the other half of the values.  because its sorted you know the best match cannot be in that half.  so with each iteration of doing this, you cut the number of values you need to search in half.  do this a few times and you’ve narrowed the number of values you need to do more expensive comparisons on to a mere handful.

so using a simple binary search algorithm you can do searching pretty quickly.  ideally you’d write some sort of spatial partitioning that considers all 3 values instead of just one value, but that is more complex and I got really good results using a simple binary search.

so anyway, the search uses the same code for the maya.cmds and api method (this code sharing is one of the many awesome things about python.  in fact I also use the same search code for tools that don’t live in maya at all).  the main difference between them is how the skin weights are actually set.  using maya.cmds.skinPercent is really slow when you’re doing it many thousands of times.  but swapping this out for the api (which isn’t quite as easy as you’d think – you basically need to restructure the entire loop to use the MIt classes for iteration) gives you the speed improvements above.  so now i just need to figure out how to speed up the search algorithm.

i could write it all in c++, but then I have to worry about things like writing data to and from disk when storing and loading weighting data.  plus its harder to share code.  the way i’ve done it now, the searching algorithms are actually used by non maya tools, so improvements there are felt across tools instead of just within maya.  so yeah, c++ sucks for all of the above.

::my verdict on pymel::

January 19th, 2010 by hamish download the zooToolBox

after using it on a fairly surface level for about a few weeks its pretty obvious that I should be using pymel.  its not easy to describe why, but I’ll take a shot.  the main reason is kinda “fluffy”, but basically it lowers mental friction when you need to interact with maya via script.  and lowering mental friction means you just get more done.

mel (and thus default maya python) is pretty straight forward – in theory.  but in practice there are all these stupid little gotchas, and most of those gotchas made their way across to the python binding of maya’s scripting interface.  awesome things like “hey I want to add an attribute.  do I use the dt or at flag?”.  or other little things like “hey I want to set a string attribute to something – don’t forget to use the -type string flag for whatever reason”, or “hey I want to query something about an attribute, which command do I use?”, or “hey, are these two objects equal?  you’ll need 10 lines of code to be sure!”.

also mel has no data structures that make sense for a 3d app.  hello?  its a 3d app, how about vector, matrix, euler and quaternion data types?  its weird that they never added these – oh and don’t tell me about the “vector” data type in mel.  its essentially useless, and seems largely unsupported.  sure the python bindings allow you to roll your own data types, but you still need to cast return values from mel commands to their appropriate type.

there are a lot of weird commands in mel, and there is a lot of mental friction involved in trying to think whether querying data about a reference is done with the file command, or the referenceQuery command.

why does pymel help?  well, in some ways it just changes the problem, but in many ways alot of things are simplified.  there is generally less typing – which again goes to the lower friction point above.  commands that return vector data actually return vector data – which means you can add and subtract them, ask for their magnitudes etc…  there are nice methods for things like querying hierarchy.  someNode.getParent() or someNode.getChildren( type=’joint’ ) for example.

of course, if you’ve already got a heap of python scripts written you may find it annoying to integrate pymel into your code.  pymel fixes a bunch of issues, and many of those fixes mean small changes which mean you can’t just drop in pymel.  and because pymel data types aren’t strings you can’t pass a PyNode to a non pymel command.  so there are a bunch of issues doing anything but a wholesale integration of pymel, and if you have a lot of code already, a wholesale integration can mean a huge amount of work.

I’ve gone the path of a partial integration.  It was originally kinda painful, but I think I’ve found most of the parts of the tangled web that is our code base that pass data across boundaries, and its working really well.  but it was initially a pain in the ass – so be warned.  I believe this problem may get worse going forward I’m not sure.

anyway, thats my experience thus far.  if you’re doing reasonably isolated tool work you should definitely be using pymel.  if you’re doing work on more integrated code then its probably not worth doing until you have a better handle on where the problems will occur.  I stupidly decided to use it on some new rigging tools which has been kinda painful tracking down all the points of failure.  and while painful so far its been worth it.

oh and I’m using 0.9.2 – the pymel guys are already way ahead with their alpha 1.0 release.

::what happened to highend3d?::

January 14th, 2010 by hamish download the zooToolBox

whatever happened to this site?  the “creative crash” site that seems to have replaced it is beyond horrible.  they’ve done a great job of making it nigh impossible to navigate, and holy cow is it BUSY!

does anyone use this resource anymore?

::Merry Christmas!::

December 23rd, 2009 by hamish download the zooToolBox

That time of year already.  Hope all you fine people out there have a safe and merry christmas this holiday period.  In fact, I hope you generally have a safe and merry time, christmas or not.

See you all in the new year!  2010.  Crazy!

::ok fine!::

December 21st, 2009 by hamish download the zooToolBox

I had already half convinced myself that I should probably try out pymel…  in fact the very act of writing my previous post pushed me right to the brink (I know, it didn’t sound like it).  after talking to a few very useful fellows, I decided what the hell. pymel 0.9.2 is now officially in our tree, and I’ll probably start tooling around with it in the new year.

more specifically, if you’re interested, I want to do a bunch of work porting the rigging code in CST over to python for a few reasons. first up its easier to write extensible code in python. if you’re at all familiar with CST code you’ll see all the horrible horrible things I’ve done using mel that mel was never intended to do – like arbitrary option parsing. at the top of every single rig primitive is no less than 20 lines of code (and quite often more like 30) just parsing options from an “option string”. this is where you can specify optional overrides to default functionality, and also allows me to easily extend the code without having to invalidate existing calls to the code.  its great that I could do this in mel, but honestly, its a hack around a fundamental hole in the language itself, plus the possible options is completely hidden from anyone not looking at the code.  so it was impossible to enumerate the optional args in a UI without duplicating code (ok not impossible – I could have written a function to parse the mel and extract the data that way…  but holy bawlz)

but more importantly I think the model CST uses is fundamentally flawed in terms of creating arbitrary rigs. anyhoo, I won’t bore you with the details (unless you really want me to) but suffice to say it will involve a bunch of code, most of which I imagine will be framework code.  I figure I may as well fold the python port into said code churn so I can move on with life.

thanks to those who provided input!

::pymel::

December 20th, 2009 by hamish download the zooToolBox

I’ve been impressed by what I’ve read about pymel from day 1. but i don’t use it. and here’s why, and I’d be interested in hearing people’s thoughts on how silly I am for passing it by.

first up – its yet another layer of abstraction, and more to the point, one which I did not write. Hence tracking down bugs is just going to be that much harder.  honestly, this argument is becoming weaker and weaker as I get more familiar with both python as a language (the pymel guys are shit hot programmery type people, so their code uses some pretty advanced python), and debugging python code.

secondly it doesn’t really save me THAT much does it?  i mean for someone like me who is super familiar with mel, using python vs mel is literally learning a few simple rules for transposing command arguments and bam.  in all but the rarest of exceptions, the python code for a command is identical in calling structure to its equivalent mel. so i would make my code slightly less verbose.  more readable code is important, but honestly, I usually abstract the calls to maya by objectifying data structures anyway.  zooTriggered for example, there is a Trigger class which provides high level access to manipulate the trigger object.  using pymel the class might contain 20% less code.  meh.

the most interesting thing seems to be the api hybridization that they’re doing which does indeed sound great, and could result in some awesome functionality. and their representation of nodes using the api instead of strings is awesome, and indeed I’ve thought about doing this myself many times, but I imagine the pymel guys have done this way better than I ever would have been able to.

lastly its not supported by autodesk so its longevity isn’t guaranteed – last time I peeked at it, it was parsing documentation to generate a bunch of code – presumably to get all the flag names.  its probably pretty unlikely that this would be a problem,  after all invalidating the pymel project would also invalidate anything that relied on the maya api, or most mel scripts, and I don’t think thats in autodesk’s best interest.

so am I high? if you’re super familiar with mel, does it really add much value?  does anyone have an example of some functionality that would have been impossible/difficult without something like pymel?

either way – pymel is a super impressive project, and a huge kudos goes to the developers, least of all because they’re giving their work over to the community, which is a huge win for everyone – especially autodesk.

::crazy use for python metaclasses::

December 19th, 2009 by hamish download the zooToolBox

i’m not a programmer, so things that might be common place to the average programmer are new and exciting for me. when python was integrated into maya, I got the chance to see what a real programming language looked like, and its been super fun discovering all these amazing concepts, which leads me to metaclasses.

browsing around on the nets it seems metaclasses aren’t generally considered practical, so maybe i’m just using em wrong, but i’ve used them on a few occasions since i discovered them, and for what i consider to be super practical things.

for example, the rigging tools I’ve written at work use them.  the rigging tools define a collection of parts. these parts know how to manage themselves, from building to aligning to rigging. in code these parts are described as classes. now writing things like GUIs to leverage the functionality of the parts I wanted to make the GUI dynamic, so that I wouldn’t ever have to maintain it after the initial writing – I wanted it to just know what parts had been defined, and display UI for those parts as appropriate.

so this is where metaclasses come in handy.  normally when you define a class what you’re doing is creating an instance of a type object. remember classes in python are objects, and a class object is an instance of the type class. so by writing your own metaclass you can “hook” into the creation of class objects. in this case I wanted to keep track of subclasses of the base class as they got defined, without having to remember to register the classes somewhere. now technically this can be done in a variety of different ways, but this is by far the most elegant and simple solution that i’ve come up with.

so on to the code?

allClasses = []
class TrackableClass(type):
    def __new__( cls, name, bases, members ):
        newCls = type.__new__( cls, name, bases, members )
        allClasses.append( newCls )
        return newCls

class BaseClass(object):
    __metaclass__ = TrackableClass
    @classmethod
    def GetSubclasses( cls ):
        return [ c for c in allClasses if issubclass( c, cls ) ]

now you can call BaseClass.GetSubclasses() and it will return a list of all classes that inherit from BaseClass. this is interesting, but not super useful, because you have to re-define the above code everywhere you want to use it. so what we can do is wrap up the above code and build the metaclass and the GetSubclass method procedurally.

def trackableClassFactory( superClass=object ):
	'''
	returns a class that tracks subclasses.  for example, if you had classB(classA)
	ad you wanted to track subclasses, you could do this:

	class classB(trackableClassFactory( classA )): pass

	a classmethod called GetSubclasses is created in the returned class for
	querying the list of subclasses
	'''
	subclassList = []
	class TrackableType(type):
		def __new__( cls, name, bases, attrs ):
			new = type.__new__( cls, name, bases, attrs )
			subclassList.append( new )

			return new

	class TrackableClass(superClass): __metaclass__ = TrackableType
	def GetSubclasses( cls ):
		'''
		returns a list of subclasses
		'''
		toReturn = []
		for c in subclassList:
			if c is cls: continue
			if issubclass( c, cls ):
				toReturn.append( c )

		return toReturn
	def GetNamedSubclass( cls, name ):
		'''
		returns the first subclass found with the given name
		'''
		for c in cls.GetSubclasses():
			if c.__name__ == name: return c

	TrackableClass.GetSubclasses = classmethod( GetSubclasses )
	TrackableClass.GetNamedSubclass = classmethod( GetNamedSubclass )

	return TrackableClass

pretty simple hey. so now you can just insert the function call above into your class’s superclass definition like so:

class SomeClass(trackableClassFactory(SuperClass)): pass

and voila! you now have the GetSubclasses method available on the class, and can query all subclasses that have been defined! neato eh?

::rigging mentor::

December 16th, 2009 by hamish download the zooToolBox

I’m kind of excited to see this happen, but more importantly I’m interested to hear from YOU (yes you, reading this, right now) as to whether you think its a good idea, and whether anybody would be interested in someone like me being involved.

the internet is a fantastic resource for learning stuff on your own, but finding things can be pretty time consuming and frustrating.  services like rigging mentor are great because they condense a heap of awesome knowledge in a single place that you can not just look at, but interact with.  hopefully they’re successful because the world is sorely lacking good technical artists and animators.  either that or they’re all avoiding me.  ;)