<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>:: m a c a r o n i K a z o o ::</title>
	<atom:link href="http://www.macaronikazoo.com/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://www.macaronikazoo.com</link>
	<description>the zoo tools development blog</description>
	<lastBuildDate>Sat, 04 Sep 2010 23:11:41 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>What is a KdTree?!</title>
		<link>http://www.macaronikazoo.com/?p=363</link>
		<comments>http://www.macaronikazoo.com/?p=363#comments</comments>
		<pubDate>Sat, 04 Sep 2010 23:11:41 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=363</guid>
		<description><![CDATA[So what is a KdTree? There are heaps of explanations you can find on the web. Wikipedia&#8217;s explanation is ok -although kinda hard to read. Anyway I figured I&#8217;d give a quick overview of what its all about. So a KdTree is a type of &#8220;spatial partitioning&#8221; data structure. You feed in a bunch of [...]]]></description>
			<content:encoded><![CDATA[<p>So what is a KdTree?  There are heaps of explanations you can find on the web.  Wikipedia&#8217;s explanation is ok -although kinda hard to read.  Anyway I figured I&#8217;d give a quick overview of what its all about.</p>
<p>So a KdTree is a type of &#8220;spatial partitioning&#8221; data structure.  You feed in a bunch of spatial data and the KdTree organizes it in a way that makes certain queries really fast.  In particular nearest neighbour searches.</p>
<p>I originally started toying with this after trying to get my skin weight loading script a bit smarter when dealing with radically different geometries.  Searching within a tolerance doesn&#8217;t work well if the geometries can be vastly different.  After all, some verts could be right next to each other, while others have matches further away.  Weighting the further points helps, but it can screw up the points that have more exact matches.</p>
<p>So how do we solve this?  Well I use an algorithm that finds the closest point, grabs the distance to this closest point and given a ratio looks for others within this radius.  You give the search function a factor &#8211; it defaults to 2 &#8211; and it sees if there are any other points within that radius.  So basically the tolerance is dynamic, not fixed.  So if the closest point is very close, the search radius used is very small.  However if the closest point is very far away, then all points within a much larger radius get considered.  It turns out this works really well when transferring vertex data like skin weighting between radically different geometries.</p>
<p>Anyway, this is totally possible to do using the binary search, but because we&#8217;re now searching twice (finds the closest then the tolerance search) we now need every single bit of speed we can extract &#8211; hence my delving into faster ways of searching.  Of course, I should probably be doing this all in c++, but&#8230;  Maybe next.</p>
<p>Let me finish off with some numbers.  On my 3 year old, dual core 2Ghz laptop, given a 10k point KdTree, I can run 10k queries on the data in about 2.2 seconds given similar inputs, or if the data is vastly different it takes about 7 seconds.  As you can see, the early out test I wrote about in the previous post can make the code up to 4 times faster.  By contrast, the binary search algorithm takes about 25 seconds.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=363</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Building classes is slow</title>
		<link>http://www.macaronikazoo.com/?p=350</link>
		<comments>http://www.macaronikazoo.com/?p=350#comments</comments>
		<pubDate>Sat, 04 Sep 2010 06:43:49 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=350</guid>
		<description><![CDATA[So I&#8217;ve been trying to write a really speedy KdTree class the last couple of nights. I think I have it as fast as I think I can make it in python (although I&#8217;d be pretty happy if you could prove me wrong), but anyway I made an unusual discovery just now. So one of [...]]]></description>
			<content:encoded><![CDATA[<p>So I&#8217;ve been trying to write a really speedy <a href="http://code.google.com/p/zootoolbox/source/browse/trunk/kdTree.py" target="_blank">KdTree</a> class the last couple of nights.  I think I have it as fast as I think I can make it in python (although I&#8217;d be pretty happy if you could prove me wrong), but anyway I made an unusual discovery just now.</p>
<p>So one of the shortcuts I put in is detecting exact point matches.  The closest point search needs to ultimately determine the squared distance to the query point.  So I figured I&#8217;d put in a test to see if that distance was zero, and because the search function is recursive, the easiest way to bail on the recurse stack is to wrap the top entry point in a try:except block, and throw a known exception when an exact match is detected.  All good.</p>
<p>But I was being a bit lazy and was declaring the class within the closest point function.  Ie the code looked something like this:</p>
<pre class="brush:py">def getClosest( self, queryPoint ):
	class ExactMatch(Exception): pass
	def search( node, depth ):
		...
		if dist == 0: raise ExactMatch  #short circuit test
		...

	try:
		search( self.rootNode, 0 )
	except ExactMatch: pass
	...</pre>
<p>You get the idea.  Anyway, so I use this for matching two pieces of arbitrary but similar geometry &#8211; so getClosest gets called heaps.  Like 5-20k times.  Adding this test saved a heap of time in the cases where lots of points were the same, but it also seemed to slow the case where none of the points were the same a lot.  Way more than I expected.  Or so I thought.</p>
<p>So after a bit more investigation I realized it wasn&#8217;t the extra test I had added, it was the ExactMatch class declaration that was doing it.</p>
<p>Lo and behold, declaring classes is SUPER slow.  Like really really slow.  You should try it out.  Go on &#8211; here is some example code</p>
<pre class="brush:py">import time
s=time.clock()
for n in range( 1000000 ):
	class ExactMatch(Exception): pass
print '%0.3g' % (time.clock()-s)</pre>
<p>Now try running that same code and instead of declaring a class, instantiate a list or something.  On my machine its around 30 times slower to declare this empty class than it is to instantiate a list.</p>
<p>Anyway &#8211; hardly a big deal, but something to keep in mind.</p>
<p>NOTE: feel free to use the kdtree code above, but you&#8217;ll either need to either use the <a href="http://code.google.com/p/zootoolbox/source/browse/trunk/vectors.py" target="_blank">Vector class here</a>, provide a get_magnitude method on your own Vector class, or tweak my code to get rid of the need for a get_magnitude call all together.  Its only used in a single place, but I&#8217;m lazy and couldn&#8217;t be bothered writing the extra line or two of code that it needs to be fully independent.  <img src='http://www.macaronikazoo.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>Oh, the code also assumes that vector points in the tree are indexable.  Ie you can get at the &#8220;X&#8221; component of a point using someVector[0].  My Vector class inherits directly from list because accessing vector members this way is way faster than having to define an attribute or worse, property.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=350</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Maya UI Wrappers</title>
		<link>http://www.macaronikazoo.com/?p=355</link>
		<comments>http://www.macaronikazoo.com/?p=355#comments</comments>
		<pubDate>Thu, 26 Aug 2010 05:17:57 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=355</guid>
		<description><![CDATA[Ever since I first started writing python code in maya I&#8217;ve been slowly building up my own UI wrappers.  I&#8217;m certainly not the only one.  Chad has done it with pymel, Byron has done it with MRV, and I&#8217;m sure there are a bunch of others.  But of course, I wasn&#8217;t using pymel back then, [...]]]></description>
			<content:encoded><![CDATA[<p>Ever since I first started writing python code in maya I&#8217;ve been slowly building up my own UI wrappers.  I&#8217;m certainly not the only one.  Chad has done it with <a href="http://code.google.com/p/pymel/" target="_blank">pymel</a>, Byron has done it with <a href="http://packages.python.org/MRV/index.html" target="_blank">MRV</a>, and I&#8217;m sure there are a bunch of others.  But of course, I wasn&#8217;t using pymel back then, nor did I know anything about MRV.</p>
<p>The good thing about my implementation however, is that its really small and self contained.  Its a single script.  Its fairly gratuitously commented, and there is a page with some tips, tricks and general high level discussion about it <a href="http://www.macaronikazoo.com/?page_id=311" target="_self">here</a>.  It a nutshell it tries to make UI authoring more like writing UI code in WX or QT.</p>
<p>Anyway, for those interested take a look at it <a href="http://code.google.com/p/zootoolbox/source/browse/trunk/baseMelUI.py" target="_blank">here</a>.  Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=355</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Oh.  Duh.</title>
		<link>http://www.macaronikazoo.com/?p=345</link>
		<comments>http://www.macaronikazoo.com/?p=345#comments</comments>
		<pubDate>Sat, 21 Aug 2010 04:06:32 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=345</guid>
		<description><![CDATA[On the new vs init thing&#8230;  Duh.  Multiple inheritance.  Separating new and init mean you can leverage more instance setup when doing multiple inheritance.  I guess I generally don&#8217;t use multiple inheritance&#8230;  But good to know. [edit:] to be more explicit &#8211; new can only be called once because it is responsible for actual instance [...]]]></description>
			<content:encoded><![CDATA[<p>On the new vs init thing&#8230;  Duh.  Multiple inheritance.  Separating new and init mean you can leverage more instance setup when doing multiple inheritance.  I guess I generally don&#8217;t use multiple inheritance&#8230;  But good to know.</p>
<p>[edit:] to be more explicit &#8211; new can only be called once because it is responsible for actual instance creation.  new does the actual object creation whereas init just takes the new instance and initializes it.  Initialization methods on multiple parent classes can both do complementary things on the same instance, so being able to split out initialization from construction makes a lot of things possible if you&#8217;re working with multiple parent classes.  Its not as useful with single inheritance &#8211; arguably it makes things more confusing.  But multiple inheritance its a godsend.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=345</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>python&#8217;s new and init</title>
		<link>http://www.macaronikazoo.com/?p=343</link>
		<comments>http://www.macaronikazoo.com/?p=343#comments</comments>
		<pubDate>Wed, 11 Aug 2010 19:42:38 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=343</guid>
		<description><![CDATA[I&#8217;ve never realized this before, but the **kw that gets passed to the __new__ method is NOT the same dictionary thats given to __init__.  Ie if you want to mess with the **kw dict in the __new__ method for some reason, you can&#8217;t because python gives __init__ a new copy of the original **kw dict [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve never realized this before, but the **kw that gets passed to the __new__ method is NOT the same dictionary thats given to __init__.  Ie if you want to mess with the **kw dict in the __new__ method for some reason, you can&#8217;t because python gives __init__ a new copy of the original **kw dict passed in.</p>
<p>Seems kinda weird to me.  Any python gurus out there that know why this is?  I&#8217;ve never really understood why python has both a __new__ and an __init__.  Why not just ditch one of them and be done with it?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=343</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>auto complete in maya&#8217;s script editor</title>
		<link>http://www.macaronikazoo.com/?p=339</link>
		<comments>http://www.macaronikazoo.com/?p=339#comments</comments>
		<pubDate>Tue, 10 Aug 2010 21:29:29 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=339</guid>
		<description><![CDATA[I just discovered this. I never realized how badly you could screw up an auto completer.  Do these people use their own software?!]]></description>
			<content:encoded><![CDATA[<p>I just discovered this.</p>
<p>I never realized how badly you could screw up an auto completer.  Do these people use their own software?!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=339</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Kahn Academy FTW!</title>
		<link>http://www.macaronikazoo.com/?p=334</link>
		<comments>http://www.macaronikazoo.com/?p=334#comments</comments>
		<pubDate>Tue, 03 Aug 2010 16:41:54 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=334</guid>
		<description><![CDATA[This is too awesome not to share &#8211; the Kahn Academy is some dude just spewing out heaps of video tutorials covering a wide variety of subjects.  Most interestingly to me at the moment, he covers a heap of linear algebra. I took linear algebra in school, but it didn&#8217;t seem at all applicable to [...]]]></description>
			<content:encoded><![CDATA[<p>This is too awesome not to share &#8211; the <a href="http://www.khanacademy.org/" target="_blank">Kahn Academy</a> is some dude just spewing out heaps of video tutorials covering a wide variety of subjects.  Most interestingly to me at the moment, he covers a heap of linear algebra.</p>
<p>I took linear algebra in school, but it didn&#8217;t seem at all applicable to life at the time so its since been purged from my brain.  My lack of 3d math skills has always been one of my weaknesses, and I try to rectify that whenever the opportunity arises.  Anyway this guy has some videos that have helped, and I figured others would probably benefit as well.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=334</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Finding Dependencies</title>
		<link>http://www.macaronikazoo.com/?p=329</link>
		<comments>http://www.macaronikazoo.com/?p=329#comments</comments>
		<pubDate>Wed, 28 Jul 2010 04:31:15 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=329</guid>
		<description><![CDATA[For some time now I&#8217;ve been almost the only one really writing python tools at work. But that has started to change recently.  So I&#8217;ve been becoming more and more concerned about the lack of formalization in the existing code base. There aren&#8217;t really any unit-tests, and there hasn&#8217;t been any way of querying what [...]]]></description>
			<content:encoded><![CDATA[<p>For some time now I&#8217;ve been almost the only one really writing python tools at work.  But that has started to change recently.  So I&#8217;ve been becoming more and more concerned about the lack of formalization in the existing code base.  There aren&#8217;t really any unit-tests, and there hasn&#8217;t been any way of querying what other tools depend on the one you&#8217;re changing.  Which of course means its easy to break things and not even know it.</p>
<p>So last week I decided to bite the bullet and learn what I could about these sorts of things.  I started off with the dependency problem because testing seemed useless without an understanding of what to test.</p>
<p>A quick google search pointed me to <a href="http://www.tarind.com/depgraph.html">this page</a>.  Not quite what I was after, but it was a fantastic piece of code to learn just how all encompassing the python standard library is.  To save you having to look at the code and trawl through it yourself, this is basically what it does.</p>
<p>In the python standard library is a module called moduleFinder. What it does is it takes a module, finds where it exists on disk, opens the file and reads it in.  This is important, at this point it has not imported the code &#8211; ie the code has not been executed.  It has simply loaded the file and read its contents into memory.  Then it takes this and compiles it into a code object using the compile builtin function.</p>
<p>Now the next bit is cool &#8211; it takes the code object, which contains the bytecode for the python code that was just compiled, and iterates over all the instructions looking for various commands &#8211; most importantly import commands.  So its pretty robust, because its using python to reverse engineer its own code and walk through the dependencies.</p>
<p>Because the code isn&#8217;t being executed you can query dependencies for maya tools without having to run the script from inside maya.  So if I make a change to say my vectors module (which is a standalone python module) the dependency query will still be able to list all the maya scripts that use this module.  This is obviously important if you&#8217;re in a studio where you have a significant portion of your python codebase outside maya.</p>
<p>Surprisingly this is super fast too, because the bytecode is literally just a stream of bytes.  So its just integer comparisons when iterating over the bytecode.  For the 320 scripts in one of our branches it takes 20 seconds to scan.</p>
<p>Now 20 seconds is still a drag, so I wrote a simple caching mechanism.  The cache basically stores a crc for each file, and that file&#8217;s immediate downstream dependencies.  When the cache is loaded all entries are checked to make sure they still exist on disk, and the crc&#8217;s are matched.  Those that have changed get re-scanned and the cache is updated.  With the caching, even a huge change that touches many files only takes a second or two to make a query against.  Which is super cool.</p>
<p>Now all I have to do is figure out how to associate a set of unit tests with each script and then the dependency query could be made to run the downstream scripts potentially affected to ensure the change is sound.  That would be cool.</p>
<p>So anyway, if you&#8217;re thinking about writing tools to query dependencies, do it &#8211; its really easy, and the link above gives you a great place to start.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=329</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Winning The Perf War</title>
		<link>http://www.macaronikazoo.com/?p=323</link>
		<comments>http://www.macaronikazoo.com/?p=323#comments</comments>
		<pubDate>Wed, 21 Jul 2010 23:17:57 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=323</guid>
		<description><![CDATA[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 [...]]]></description>
			<content:encoded><![CDATA[<p>So one super convenient thing pymel gives you is a name independent way of tracking nodes in your code.  So doing this:</p>
<pre class="brush:py">
from maya.cmds import *
o = group( em=True )
rename( o, 'something_else' )
select( o )
</pre>
<p>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.</p>
<p>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.</p>
<p>Anyhoo, so poking around a bit I experimented with adding a few methods to MObject, kinda like this:</p>
<pre class="brush:py">
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()
</pre>
<p>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.</p>
<p>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:</p>
<pre class="brush:py">
from maya.cmds import *
o = asMObject( group( em=True ) )
rename( o, 'something_else' )
select( o )
</pre>
<p>Plus you can compare two nodes without having to worry about whether one is a full path to the node or a partial path.</p>
<p>This code can be packaged up into a simple little module that you can import at will &#8211; 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:</p>
<pre class="brush:py">from apiExtensions import asMObject</pre>
<p>And wham &#8211; you get the rest for free.</p>
<p>Casting to these MObjects is still slow &#8211; there&#8217;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 &#8211; again without having to wrap anything.</p>
<p>I&#8217;m smelling win.</p>
<p>Thoughts?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=323</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Not to be repetitive</title>
		<link>http://www.macaronikazoo.com/?p=309</link>
		<comments>http://www.macaronikazoo.com/?p=309#comments</comments>
		<pubDate>Tue, 20 Jul 2010 19:47:17 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=309</guid>
		<description><![CDATA[But pymel really is horribly slow. Perhaps I&#8217;m doing something wrong &#8211; although if I am I&#8217;m pretty sure I&#8217;m not alone. But I just spent a few hours tearing it out of my rigging tool. There is still a long way to go, but all of a sudden the tool is unbelievably faster. Like [...]]]></description>
			<content:encoded><![CDATA[<p>But pymel really is horribly slow.  Perhaps I&#8217;m doing something wrong &#8211; although if I am I&#8217;m pretty sure I&#8217;m not alone.  But I just spent a few hours tearing it out of my rigging tool.  There is still a long way to go, but all of a sudden the tool is unbelievably faster.  Like to the point where I no longer have to spend time trying to optimize the tool.</p>
<p>So be warned people!  Pymel might make for more readable code, but there is a huge cost.  HUGE.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=309</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>more pymel speed findings</title>
		<link>http://www.macaronikazoo.com/?p=290</link>
		<comments>http://www.macaronikazoo.com/?p=290#comments</comments>
		<pubDate>Tue, 20 Jul 2010 00:36:59 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=290</guid>
		<description><![CDATA[I started poking the speed problems that plague pymel a bit more. It seems the problem may lie in the simple fact that instantiating MObject&#8217;s is expensive&#8230; try it out: from maya.OpenMaya import * import time import maya.cmds as cmd def asMObject( item ): sel = MSelectionList() sel.add( item ) tmp = MObject() sel.getDependNode( 0, [...]]]></description>
			<content:encoded><![CDATA[<p>I started poking the speed problems that plague pymel a bit more.  It seems the problem may lie in the simple fact that instantiating MObject&#8217;s is expensive&#8230;  try it out:</p>
<pre class="brush:py">
from maya.OpenMaya import *

import time
import maya.cmds as cmd

def asMObject( item ):
	sel = MSelectionList()
	sel.add( item )

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

	return tmp

def test():
	start = time.clock()
	for obj in cmd.ls():
		asMObject( obj )

	print 'time taken %0.3f' % (time.clock()-start)
</pre>
<p>This is pretty simple stuff.  Simply iterating over all nodes in a blank scene and casting them to MObjects individually is slow.  It takes 0.05 seconds on my machine on an empty scene.  Doing the same thing with pymel takes about 0.21 seconds which is a little more than 4 times slower than this.  Quite a leap from 350x.</p>
<p>So it seems Autodesk is largely to blame for some bloated script bindings.</p>
<p>From this little bit of investigation I&#8217;m not really convinced that its possible to have a fast unified abstraction of mel/api without help from Autodesk in cleaning up and speeding up their wrappers.</p>
<p>What might be interesting is looking into subclassing unicode and lazily populating the instance with api data as its requested from script to minimize the time spent instantiating maya&#8217;s object types.  Incidentally pymel&#8217;s PyNode class used to inherit from unicode, but I&#8217;m unaware of how aggressive it was with lazy evaluation.  I believe the reason Chad moved away from this idea in pymel was because strings in python are immutable and node names in maya are not.  Having an immutable object represent mutable data is kinda sucky.  But hey, we have to deal with this currently anyway, so its not like we&#8217;re moving backwards.  Just not as far forward as we&#8217;d like.</p>
<p>The sucky thing about doing the lazy evaluation thing is that it&#8217;d be tricker to leverage inheritance &#8211; you wouldn&#8217;t want to determine the most appropriate class to cast the node as on instantiation because thats where it becomes expensive.  So instead you&#8217;d have to dynamically populate the instance dictionary as functionality was requested.</p>
<p>Anyway&#8230;  Once again we&#8217;re at the mercy of some shitty corner cutting from Autodesk.</p>
<p>In other news &#8211; has anyone seen how WELL blender has integrated scripting?  Way better than any dcc app I&#8217;ve ever seen.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=290</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>no mo callable?!</title>
		<link>http://www.macaronikazoo.com/?p=285</link>
		<comments>http://www.macaronikazoo.com/?p=285#comments</comments>
		<pubDate>Mon, 19 Jul 2010 20:21:20 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=285</guid>
		<description><![CDATA[why did they remove callable( someObj ) in favor of hasattr( someObj, '__call__' )??? that&#8217;s horrible to write and to read! ref: porting code to python 3]]></description>
			<content:encoded><![CDATA[<p>why did they remove <code>callable( someObj )</code> in favor of <code>hasattr( someObj, '__call__' )</code>???  that&#8217;s horrible to write and to read!  <img src='http://www.macaronikazoo.com/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' /> </p>
<p>ref: <a href="http://diveintopython3.org/porting-code-to-python-3-with-2to3.html#callable">porting code to python 3</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=285</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>more thoughts on pymel</title>
		<link>http://www.macaronikazoo.com/?p=271</link>
		<comments>http://www.macaronikazoo.com/?p=271#comments</comments>
		<pubDate>Mon, 19 Jul 2010 19:31:14 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=271</guid>
		<description><![CDATA[So after using pymel for a bunch I&#8217;m done with it.  Why you ask? For starters its kinda this weird middle ground where its not mel (maya.cmds) and its not the api.  You can&#8217;t easily mix it with maya.cmds without having to throw in explicit str() casts all over your code, and to put isinstance( [...]]]></description>
			<content:encoded><![CDATA[<p>So after using pymel for a bunch I&#8217;m done with it.  Why you ask?</p>
<p>For starters its kinda this weird middle ground where its not mel (maya.cmds) and its not the api.  You can&#8217;t easily mix it with maya.cmds without having to throw in explicit str() casts all over your code, and to put isinstance( obj, PyNode ) checks everywhere which is messy and error prone.  You also can&#8217;t easily mix it with the api directly.</p>
<p>So you&#8217;re kinda left in this unusual grey zone.  PyNode objects do carry along with them some handles to API objects like their mobject and others depending on the PyNode in question, but again, you have to speckle you code with attribute references like someNode.__apiobject__ etc, if you want to call into anything inside maya.OpenMaya.</p>
<p>But most importantly, it is amazingly slow.  Like mind bogglingly slow when compared to mel. Exactly whats going on I haven&#8217;t spent the time to discover, but here is a very simple, albeit contrived demonstration of its tardiness.</p>
<pre class="brush:py">
import time
import maya.cmds as cmd
MAX = 1000
start = time.clock()
for n in xrange( MAX ):
	cmd.ls()
print 'time taken %0.3f' % (time.clock()-start)

from pymel.core import ls
start = time.clock()
for n in xrange( MAX ):
	ls()  #NOTE: this is using pymel’s wrapping of the ls command
print 'time taken %0.3f' % (time.clock()-start)
</pre>
<p>On my machine the first test which uses just <strong>mel takes 0.07 seconds</strong> on a completely empty scene.  The second test, which uses <strong>pymel, takes a whopping 24 seconds</strong>.</p>
<p>That is a <strong>350x speed difference</strong>.  That&#8217;s HUGE!</p>
<p>Now, originally when I started writing this blog post I hadn&#8217;t done the speed tests.  My assumption was that it was slower, but the slow down was probably merely a factor of 2 due to the way pymel has to cast return values from strings, back to MObjects.  But I figured instead of writing the blog post with assumptions in it, I should at least do the work to get some actual numbers.</p>
<p>The question now becomes, do the benefits of pymel outweigh the negatives?  In my case I believe the answer is a resounding NO.</p>
<p>So after using it for as long as I have I&#8217;m kinda backpedaling.  Its going to suck pulling pymel out of the tree, but clearly it has to be done.  I&#8217;ve written a heap of rigging code using it, and have been wondering why it has been running so slow.  My assumption was that it was my fault, and that somewhere I was writing some super redundant code.  Now I know better.</p>
<p>This is a pretty big shame &#8211; I like what I gain from pymel, but I don&#8217;t like the constraints it puts on me, and the speed hit is unacceptable.</p>
<p>I do however, have some ideas on what might be a better solution to wrapping mel in a nicer way.  It seems to me that extending the MObject class and making it really easy to cast strings to MObject instances would be a cool way to go.  The wrapped objects would be native MObjects, so they&#8217;d be first class API citizens, and you could cast them to strings easily to pass back to mel should you need to. I&#8217;ve done some tests on the idea and so far it seems promising.  I have a feeling I won&#8217;t ever have a solution as comprehensive as pymel, but at the same time I&#8217;m not sure thats what I want anyway.</p>
<p>Anyway, I just wanted to put my findings out there for anyone else who was shopping around for opinions on whether to use pymel or not.  Consider yourself warned.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=271</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>holy shit modo sucks!</title>
		<link>http://www.macaronikazoo.com/?p=261</link>
		<comments>http://www.macaronikazoo.com/?p=261#comments</comments>
		<pubDate>Sun, 06 Jun 2010 20:13:06 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=261</guid>
		<description><![CDATA[Where to begin. Actually, let me preface this with the statement that I&#8217;m talking as a technical artist, not a modeler. Modo is a pretty decent modeler, but as someone who has to shoehorn the thing into a production pipeline its probably one of the poorest applications I&#8217;ve ever encountered. Seriously. Lets see, there is [...]]]></description>
			<content:encoded><![CDATA[<p>Where to begin.</p>
<p>Actually, let me preface this with the statement that I&#8217;m talking as a technical artist, not a modeler.  Modo is a pretty decent modeler, but as someone who has to shoehorn the thing into a production pipeline its probably one of the poorest applications I&#8217;ve ever encountered.  Seriously.</p>
<p>Lets see, there is no documentation.  Their scripting syntax is just god awful bad.  Setting a value or performing an action via script is COMPLETELY DIFFERENT from querying data about the current state of what you&#8217;re just about to change.</p>
<p>An example here helps demonstrate &#8211; I want to query the name of something, so I use the command:</p>
<p><code>query scenesystem item.name &lt;theItemId&gt;</code></p>
<p>this will give me the name of the item &#8220;theItemId&#8221;.  To change the name of the item however, I need to use two commands like so:</p>
<p><code>select.subItem &lt;theItemId&gt; set mesh<br />
item.name "holy shit modo sucks ballz!"</code></p>
<p>So not only is there absolutely no symmetry between querying and editing information in your scene, but there are these cryptic systems like sceneservice, renderservice, layerservice etc &#8211; none of which are documented, and good luck figuring out which service is responsible for what.</p>
<p>And holy wow how bad is authoring UI?  Not only is it NOT POSSIBLE to script UI&#8217;s (so no dynamic UIs for you!), but its also a matter of hand editing xml files.  And again &#8211; who the hell knows what any of the keys are for.  But whats super awesome about this &#8211; if you get something &#8220;wrong&#8221; modo doesn&#8217;t give you errors, or warnings or anything helpful.  It just does nothing.  So you&#8217;re never quite sure what the hell is going on.</p>
<p>Incidentally, there is a &#8220;Form Editor&#8221; which will let you build buttons with macros in them.  But thats it.  Oh and there is no such thing as a checkbox widget either.  Just a widget and if you give the widget a command which returns a bool then the widget magically knows to be a checkbox.  And how do you define these variables you ask?  Oh thats ALSO fun and ALSO involves hand editing xml files and hoping like a fancy pig that you divined the correct magics modo is expecting.</p>
<p>I could go on but honestly, what the hell is wrong with the people at luxology?  They have clearly put a heap of thought into modo.  As a modeler its quite a deep and sort of accessible tool, but what the fuck are they thinking about their scripting?  Like not only is it bad, but its almost like its designed to be really bad.  I find it hard to believe that someone honestly things any of the choices they&#8217;ve made are good.</p>
<p>Its even more baffling when you look at how RIGHT the blender guys have done it.</p>
<p>Whats your story luxology?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=261</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>more python love</title>
		<link>http://www.macaronikazoo.com/?p=258</link>
		<comments>http://www.macaronikazoo.com/?p=258#comments</comments>
		<pubDate>Sat, 05 Jun 2010 20:27:09 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=258</guid>
		<description><![CDATA[Man, I&#8217;m such a python fanboy&#8230;  Its such an enabling language.  And I say this as someone who is trying to be productive in c++ as well.  c++ is about as accessible as two inaccessible things nailed together. Anyhoo, so whats with the lovin this time?  Well the other day I wanted to get a [...]]]></description>
			<content:encoded><![CDATA[<p>Man, I&#8217;m such a python fanboy&#8230;  Its such an enabling language.  And I say this as someone who is trying to be productive in c++ as well.  c++ is about as accessible as two inaccessible things nailed together.</p>
<p>Anyhoo, so whats with the lovin this time?  Well the other day I wanted to get a clear picture of what part of my python code base was being imported.  I was trying to make sure certain parts of my tools were pulling code only from a specific part of the tree in order to better isolate things.  Anyway I was kinda dreading the task, and figured it&#8217;d be a whole lot of no fun.  I had heard of import hooks, but knew nothing of them.</p>
<p>So 20 minutes later my problem was solved and I had a super convenient, generalized way of spewing all imports that were happening.  Furthermore it was a simple matter of running wing, setting a breakpoint in my import hook and bam.  The problem was solved in all of an hour, plus I have a convenient way of ensuring thing don&#8217;t creep in.  The import hook automatically turns itself on for me (or anyone else who sets the appropriate env var) so when I developing code I get a pretty rude warning if I start using code outside the desired part of the tree.</p>
<p>Sounds kinda weird &#8211; but the main point of it is we have a bunch python code which is shared across all projects studio-wide, and used mainly for tools like maya, modo, blender etc.  But we don&#8217;t want our project specific tools to rely on these pieces of code, otherwise people who aren&#8217;t content creators are no longer able to re-compile and optimize certain classes of assets.</p>
<p>Anyway, once again, python rocks.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=258</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>more use for trackableClassFactory</title>
		<link>http://www.macaronikazoo.com/?p=250</link>
		<comments>http://www.macaronikazoo.com/?p=250#comments</comments>
		<pubDate>Tue, 06 Apr 2010 17:28:10 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=250</guid>
		<description><![CDATA[This isn&#8217;t a critically useful use of the trackableClassFactory code I blogged about previously, but its kinda nice &#8211; and makes for less busywork when writing python plugins in maya.  Which of course means its less of a chore. So basically what I do is I have a base class that implements some simple class [...]]]></description>
			<content:encoded><![CDATA[<p>This isn&#8217;t a critically useful use of the <a href="http://www.macaronikazoo.com/?p=171" target="_self">trackableClassFactory code I blogged about</a> previously, but its kinda nice &#8211; and makes for less busywork when writing python plugins in maya.  Which of course means its less of a chore.</p>
<p>So basically what I do is I have a base class that implements some simple class methods like SyntaxCreator for the given class variable containing the syntax definition for the MPxCommand you&#8217;re writing.  This class also does things like auto hook up a &#8220;-h/-help&#8221; flag so that all commands defined in the plugin behave in the same way.</p>
<p>Then the initializePlugin function simply iterates over all subclasses of this base class, instantiates them and registers them as commands.  Similarly with the uninitializePlugin function.  So now to add a new command to the plugin you simply have to inherit from the base class and write your doIt method and describe the flags you want used.  The new command gets registered using the name you gave the class (although you *can* override this if you want) and everything just gets setup for you.</p>
<p>nice!  take that c++!</p>
<p>I&#8217;m sure this is possible in c++ but I&#8217;m pretty sure its going to be more than the 100 lines of code this solution requires.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=250</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>c++ is no fun</title>
		<link>http://www.macaronikazoo.com/?p=248</link>
		<comments>http://www.macaronikazoo.com/?p=248#comments</comments>
		<pubDate>Tue, 06 Apr 2010 07:21:25 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=248</guid>
		<description><![CDATA[wow, c++ is no fun to work with&#8230; admittedly I&#8217;m pretty n00b, so perhaps its just an unfamiliarity thing, but holy wow, there is a hell of a lot of busy work writing c++ code. everything takes like 10 times more typing. here&#8217;s to hoping the pypy guys can make python mind numbingly fast.]]></description>
			<content:encoded><![CDATA[<p>wow, c++ is no fun to work with&#8230;  admittedly I&#8217;m pretty n00b, so perhaps its just an unfamiliarity thing, but holy wow, there is a hell of a lot of busy work writing c++ code.  everything takes like 10 times more typing.</p>
<p>here&#8217;s to hoping the <a href="http://codespeak.net/pypy/dist/pypy/doc/" target="_blank">pypy</a> guys can make python mind numbingly fast.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=248</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>UI &#8220;Events&#8221; in maya</title>
		<link>http://www.macaronikazoo.com/?p=246</link>
		<comments>http://www.macaronikazoo.com/?p=246#comments</comments>
		<pubDate>Thu, 11 Mar 2010 06:19:59 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=246</guid>
		<description><![CDATA[I have a feeling UI programming in maya will actually not suck anywhere near as much with 2011 coming along soon.  I&#8217;m not 100% sure, but it sounds as though you&#8217;ll be able to use the scripted bindings for QT directly in maya for UI work in 2011.  If this is true &#8211; and I&#8217;m [...]]]></description>
			<content:encoded><![CDATA[<p>I have a feeling UI programming in maya will actually not suck anywhere near as much with 2011 coming along soon.  I&#8217;m not 100% sure, but it sounds as though you&#8217;ll be able to use the scripted bindings for QT directly in maya for UI work in 2011.  If this is true &#8211; and I&#8217;m not sure it is &#8211; then things will certainly get a lot more exciting.  I&#8217;ve been restricted many times by maya&#8217;s lack of decent UI in the past, and integrating something like wxpython, while possible, is also a drag.</p>
<p>So anyway, this last week I was writing some UI to a tool that isn&#8217;t really finalized yet.  I want to keep the UI code flexible and modular so that its easy to change it up without creating a bunch of work.  Anyway if you&#8217;ve ever programmed UI using WX or QT there is this concept of events.  Basically pieces of UI can listen to events of a certain kind and define a function that gets called when that widget &#8220;hears&#8221; such an event.  Event driven UI&#8217;s are pretty common and make for a really powerful way of expressing action and reaction without having to make widgets aware of one another.  Because you&#8217;re not defining the relationship between widgets, doing things like radically changing up UI layouts is easier to achieve.</p>
<p>Anyway, so I wanted something like this in maya for my mel UIs.  So I made the simplest solution on the planet &#8211; which actually works out really well.  Basically I have a bunch of classes I wrote that wrap the maya mel UI widgets.  The base class has a sendEvent method and a process event method.  The sendEvent method basically just walks up the UI hierarchy and runs processEvent with the given event args.  And thats it.  Super simple, but it means I can write modular UI classes, and glue them together easily, or break off a piece of UI into a separate window etc.</p>
<p>Previously I&#8217;ve had to have child UI hold a handle to parent UI to call update functions or vice versa which makes this sort of modularization annoying at best, impossible at worst.  So anyway, super low tech simple solution &#8211; works a charm.  Anyone got a better idea?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=246</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>skinned geo in modo</title>
		<link>http://www.macaronikazoo.com/?p=244</link>
		<comments>http://www.macaronikazoo.com/?p=244#comments</comments>
		<pubDate>Sat, 27 Feb 2010 19:58:06 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=244</guid>
		<description><![CDATA[I&#8217;m not really a modo user &#8211; I&#8217;ve had to write a few scripts for it, but generally I&#8217;m considered a modo n00b.  But its quite a popular modeling app at work, which is hardly surprising given how much better it looks/sounds than maya for modeling.  So anyway, it occurred to me that I could [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m not really a modo user &#8211; I&#8217;ve had to write a few scripts for it, but generally I&#8217;m considered a modo n00b.  But its quite a popular modeling app at work, which is hardly surprising given how much better it looks/sounds than maya for modeling.  So anyway, it occurred to me that I could use a modified version of the skin weight saving tool that I wrote for maya, as a way of creating skinning information for geometry out of modo.</p>
<p>How?</p>
<p>Well, we have a compilation step between export and visualization in the game engine.  This isn&#8217;t necessary, but it provides a convenient way of doing rigid error checking, and expensive optimization up front &#8211; and as of late we&#8217;ve been using it as a place to do additional assembly on the exported data.</p>
<p>When data gets exported out of the authoring app we try to preserve as much as the scene as possible &#8211; which is useful for a variety of reasons which I won&#8217;t go into here.  But for runtime efficiency, you generally want to do the opposite &#8211; rigorously remove anything that isn&#8217;t essential.  Some studios do this in their exporter, some studios do this at load time in the engine &#8211; we do it as a post export step before loading it in engine, which as I mentioned, provides a convenient place to do additional assembly on data that comes from multiple sources.</p>
<p>Getting back to the original point of the post &#8211; skinning geometry in modo.  Well, technically the skinning happens outside of modo, but all the data is authored inside modo, so it IS only a technicality.</p>
<p>First up &#8211; the skinning tool in maya is a point cloud loading and saving tool.  So basically you give it some skinned geometry, and the tool writes out position vectors with a list of joint names and joint weights &#8211; and thats it.  So if we can get that information authored in modo, then we can derive skinning data.</p>
<p>In modo what an artist would author is a hierarchy of transforms (I&#8217;m not sure if they&#8217;re called that in modo &#8211; modo isn&#8217;t that strong on clear terminology from what I&#8217;ve seen of it), and then parent a bunch of primitive geometry to those transforms.  The empty transforms get named in a special way (like bone_pelvis, or joint_arm_L etc) so the tool that derives the skinning data knows what to interpret as the skeleton, and then any geometry parented under that transform is assumed to be rigidly skinned to that joint.</p>
<p>So this is enough data to generate a point cloud that can be used to generate skinning information.  The skin weight tool then looks through the point cloud data, and does a radial search based on a couple of user settings for each vertex in the actual geometry to be skinned.  Generally what the search does is expand a starting radius until its found 2 or more verts.  Then it finds the closest verts *(see below for details on what this means), averages their weighting contribution and applies the skinning data.</p>
<p>So its kinda like sculpting volumes &#8211; and in fact, its a really fast and easy way of generating skinning information.</p>
<p>* The tool, to find the &#8220;closest verts&#8221; works kinda like this:  Given a vert on the actual geometry, it starts off with a radius of x.  The radius is grown each iteration until multiple verts are found.  From these verts, the closest is found.  The distance to this closest vert is stored, and the distances to all the other verts is then compared to a ratio.  Any vert that falls within the &lt;closest distance&gt;*&lt;ratio&gt; range is included in the weight sum (weighted by proximity).  By doing it this way scale isn&#8217;t important, and the user is presented with two fairly intuitive values to control the weighting.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=244</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>check it out &#8211; video tutorials!</title>
		<link>http://www.macaronikazoo.com/?p=240</link>
		<comments>http://www.macaronikazoo.com/?p=240#comments</comments>
		<pubDate>Fri, 05 Feb 2010 01:37:30 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=240</guid>
		<description><![CDATA[super cool &#8211; the rigging mentor dudes have made a video tutorial for zooPickwalk!  its way cool &#8211; way cooler than anything I would have ever done.  so head on over and check it out.]]></description>
			<content:encoded><![CDATA[<p>super cool &#8211; the rigging mentor dudes have made a <a href="http://www.youtube.com/watch?v=zq41DNrJWR8" target="_blank">video tutorial for zooPickwalk</a>!  its way cool &#8211; way cooler than anything I would have ever done.  so head on over and check it out.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=240</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>rigging dojo is a go</title>
		<link>http://www.macaronikazoo.com/?p=237</link>
		<comments>http://www.macaronikazoo.com/?p=237#comments</comments>
		<pubDate>Wed, 03 Feb 2010 17:40:18 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=237</guid>
		<description><![CDATA[so the Rigging Dojo guys have officially launched their service.  wish em luck (good luck!), and if you&#8217;re trying to get into da biz, check em out, see if they work for you.]]></description>
			<content:encoded><![CDATA[<p>so the <a href="http://www.riggingdojo.com/" target="_blank">Rigging Dojo</a> guys have officially launched their service.  wish em luck (good luck!), and if you&#8217;re trying to get into da biz, check em out, see if they work for you.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=237</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>fast skin loading!</title>
		<link>http://www.macaronikazoo.com/?p=232</link>
		<comments>http://www.macaronikazoo.com/?p=232#comments</comments>
		<pubDate>Fri, 29 Jan 2010 19:55:05 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=232</guid>
		<description><![CDATA[I finally spent a bit of time re-implementing my skin weight loading script in the api &#8211; 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 [...]]]></description>
			<content:encoded><![CDATA[<p>I finally spent a bit of time re-implementing my skin weight loading script in the api &#8211; 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.</p>
<p>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&#8217;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 &#8211; which is not huge, but nothing to be scoffed at either.  lets looks at some details.</p>
<p>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 &#8211; 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.</p>
<p>so how does the searching work?  well basically it uses a binary search algorithm on the point cloud.  what does this mean?  i didn&#8217;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&#8217;ve narrowed the number of values you need to do more expensive comparisons on to a mere handful.</p>
<p>so using a simple binary search algorithm you can do searching pretty quickly.  ideally you&#8217;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.</p>
<p>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&#8217;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&#8217;re doing it many thousands of times.  but swapping this out for the api (which isn&#8217;t quite as easy as you&#8217;d think &#8211; 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.</p>
<p>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&#8217;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.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=232</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>my verdict on pymel</title>
		<link>http://www.macaronikazoo.com/?p=224</link>
		<comments>http://www.macaronikazoo.com/?p=224#comments</comments>
		<pubDate>Wed, 20 Jan 2010 05:45:31 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=224</guid>
		<description><![CDATA[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&#8217;ll take a shot.  the main reason is kinda &#8220;fluffy&#8221;, but basically it lowers mental friction when you need to interact with maya via script.  and lowering [...]]]></description>
			<content:encoded><![CDATA[<p>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&#8217;ll take a shot.  the main reason is kinda &#8220;fluffy&#8221;, 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.</p>
<p>mel (and thus default maya python) is pretty straight forward &#8211; 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&#8217;s scripting interface.  awesome things like &#8220;hey I want to add an attribute.  do I use the dt or at flag?&#8221;.  or other little things like &#8220;hey I want to set a string attribute to something &#8211; don&#8217;t forget to use the -type string flag for whatever reason&#8221;, or &#8220;hey I want to query something about an attribute, which command do I use?&#8221;, or &#8220;hey, are these two objects equal?  you&#8217;ll need 10 lines of code to be sure!&#8221;.</p>
<p>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 &#8211; oh and don&#8217;t tell me about the &#8220;vector&#8221; 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.</p>
<p>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.</p>
<p>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 &#8211; which again goes to the lower friction point above.  commands that return vector data actually return vector data &#8211; which means you can add and subtract them, ask for their magnitudes etc&#8230;  there are nice methods for things like querying hierarchy.  someNode.getParent() or someNode.getChildren( type=&#8217;joint&#8217; ) for example.</p>
<p>of course, if you&#8217;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&#8217;t just drop in pymel.  and because pymel data types aren&#8217;t strings you can&#8217;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.</p>
<p>I&#8217;ve gone the path of a partial integration.  It was originally kinda painful, but I think I&#8217;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 &#8211; so be warned.  I believe this problem may get worse going forward I&#8217;m not sure.</p>
<p>anyway, thats my experience thus far.  if you&#8217;re doing reasonably isolated tool work you should definitely be using pymel.  if you&#8217;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.</p>
<p>oh and I&#8217;m using 0.9.2 &#8211; the pymel guys are already way ahead with their alpha 1.0 release.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=224</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>what happened to highend3d?</title>
		<link>http://www.macaronikazoo.com/?p=226</link>
		<comments>http://www.macaronikazoo.com/?p=226#comments</comments>
		<pubDate>Thu, 14 Jan 2010 17:27:29 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=226</guid>
		<description><![CDATA[whatever happened to this site?  the &#8220;creative crash&#8221; site that seems to have replaced it is beyond horrible.  they&#8217;ve done a great job of making it nigh impossible to navigate, and holy cow is it BUSY! does anyone use this resource anymore?]]></description>
			<content:encoded><![CDATA[<p>whatever happened to this site?  the &#8220;creative crash&#8221; site that seems to have replaced it is beyond horrible.  they&#8217;ve done a great job of making it nigh impossible to navigate, and holy cow is it BUSY!</p>
<p>does anyone use this resource anymore?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=226</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Merry Christmas!</title>
		<link>http://www.macaronikazoo.com/?p=218</link>
		<comments>http://www.macaronikazoo.com/?p=218#comments</comments>
		<pubDate>Thu, 24 Dec 2009 03:16:12 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=218</guid>
		<description><![CDATA[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!]]></description>
			<content:encoded><![CDATA[<p>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.</p>
<p>See you all in the new year!  2010.  Crazy!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&amp;p=218</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
