Archive for December, 2009

Merry Christmas!

Wednesday, December 23rd, 2009

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!

Monday, December 21st, 2009

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

Sunday, December 20th, 2009

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

Saturday, December 19th, 2009

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 dojo

Wednesday, December 16th, 2009

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 dojo 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.  ;)

make ur own ebooks!

Monday, December 14th, 2009

nothing to do with anything I usually bollocks on about, but pure awesome nonetheless.  make your own book scanner!  I loooove my kindle – not because its a kindle, but because it lets me carry around a crap load of books with me, or any other reading material for that matter.  but what about the books you already have, or even worse, when someone gives you a *real* book as a gift or something?

could be a fun little weekend project methinks.  very cool!

blender 2.5 looking wow

Sunday, December 6th, 2009

I’ve yet to do anything serious in it, but poking around it looks fantastic.  looks like they’ve got all the right hooks in there to integrate as tightly with your pipeline as you need, and the new design of the ui and scripting system looks really nice, documentation seems filled with examples, the in-blender console looks great too.

again, this is only what it looks like, I still haven’t done anything serious with it.  and in fact, by default there are some annoyances, but it looks entirely possible to script your way around them – and lets face it, after using maya for as many years as I have, scripting my way around shitty default interfaces has fooled some people into thinking I know what I’m doing.  ;)

anyway, if you have a chance, take a look.  its not perfect, but it is super exciting.  way more exciting than the tens of thousands of dollars that’ve been handed over to autodesk.

changing skeleton after binding

Saturday, December 5th, 2009

sometimes I just feel slow – I expect alot of you already know this, but its new to me – and super useful.  just say you’ve skinned up your dude and spent a bunch of time getting things looking sha-weet! and you suddenly realize that where you placed the clavicles is kinda dumb.

well of course, you could use maya’s move joint without affecting skin tool, but its a bit of a shit tool imho – its super clunky, you can’t do things like rotate freeze transform etc – it simply lets you move.  you could *also* use something like zooWeightSave to store weights, blow weights away, change skeleton reapply weights…  but gah!  thats more than one step!

anyhoo, so the following script will take the current pose of the skeleton and redefine it as the bind pose, and resets the skin.  nothing else is changed, so your weighting etc is untouched.  in fact the skin cluster doesn’t even need to be turned off.

def resetSkinCluster( skinCluster ):
	'''
	splats the current pose of the skeleton into the skinCluster - ie whatever
	the current pose is becomes the bindpose
	'''
	nInf = len( cmd.listConnections( '%s.matrix' % skinCluster, destination=False ) )
	for n in range( nInf ):
		try:
			slotNJoint = cmd.listConnections( '%s.matrix[ %d ]' % (skinCluster, n), destination=False )[ 0 ]
		except IndexError: continue

		matrixAsStr = ' '.join( map( str, cmd.getAttr( '%s.worldInverseMatrix' % slotNJoint ) ) )
		melStr = 'setAttr -type "matrix" %s.bindPreMatrix[ %d ] %s' % (skinCluster, n, matrixAsStr)
		#melStr = 'setAttr -type "matrix" %s.bindPose %s' % (slotNJoint, matrixAsStr)
		mel.eval( melStr )

		#reset the stored pose in any dagposes that are conn
		for dPose in cmd.listConnections( skinCluster, d=False, type='dagPose' ) or []:
			cmd.dagPose( slotNJoint, reset=True, n=dPose )

anyway, I actually use this as a step in our auto-rigger.  basically before a skeleton is auto-rigged, it gets checked to ensure alignment of everything is kosher – and if not it fixes things up and resets the bind pose.  so modelers and animators don’t have to worry about joint alignment, they just need to worry about placement (which is much easier to get right), and alignment is taken care of auto-magically.  placement can also be changed just as easily – because the rig is auto generated they can change the placement of a clavicle and hit auto-rig and the clav placement gets updated and the rig rebuilt to suit.

anyway – its an awesome time saver.  I plan to write functionality that will emulate maya’s crappy joint re-placement tool. but it’ll be state based: so turn skin off, change skel with any normal maya tools (freeze transforms as well) then re-enable skinning.

funny

Friday, December 4th, 2009

every now and then I browse through stats for this site. anyhoo I was having a look at the search queries that have ultimately led users to my site, and apparently “super big ass” is at number 27, and “holy ass” is at number 55. admittedly those queries only resulted in 3 and 1 hit respectively.

anyway, not wanting to be rude, if you’re here coz you’re looking for super big ass, a big hi from me!

flushing python code

Friday, December 4th, 2009

so maybe I’m just slow, but I figured out how to get python to purge its previously imported modules today. this is super useful for developing any sort of python script that has dependencies that are also changing.

without flush you have to make sure you reload all the modules that are changing, and you usually need to do it in the right order – as code objects hold references to module objects. so if you have moduleA that imports moduleB, they both change but you reload moduleA and then moduleB – the freshly reloaded moduleA will still hold references to the old versions of moduleB. if you don’t realize this, you will most likely end up with less hair by the end of the day than you started with. noticeably less.

anyhoo – its super simple, which is why i feel like a retard for not having thought of it before. there are a couple of gotchas – binary modules don’t seem to flush, so you may need to put in special cases for them (i just remove any binary module names from the keysToDelete list before doing the removal).

and I guess the other gotcha is that this uses the filesystem module in the zooToolbox. there is no reason you can’t roll your own though.

def flush():
	'''
	flushes all loaded modules from sys.modules which causes them to be reloaded
	when next imported...  super useful for developing crap within a persistent
	python environment
	'''
	dirsToFlush = filesystem.Path( 'd:/tools' ), filesystem.Path( 'd:/studio/maya' )

	keysToDelete = []
	for modName, mod in sys.modules.iteritems():
		try:
			modPath = filesystem.Path( mod.__file__ )
		except AttributeError: continue

		for flushDir in dirsToFlush:
			if modPath.isUnder( flushDir ):
				keysToDelete.append( modName )
				break

	for keyToDelete in keysToDelete:
		del( sys.modules[ keyToDelete] )

	gc.collect()