Sat, Feb 6th, 2016
posted by jjburton 10:02 PM

As I’ve been closing in on finishing Morpheus 2 I found myself in need of a distributable skin data system to be able to apply skinning information to Morphy meshes after they’d been customized and no longer matched up with the base mesh. Not being able to find a good way of doing it in natively to Maya and not finding any open source options, writing our own was the only way forward.

Thanks to Alex Widener and Chad Vernon for some tech help along the way.

Before delving in here, here’s some lessons learned along the way.

  • mc.setAttr — found this to be a unreliable method of setting weights via the ‘head_geo_skinNode.weightList[0].weights[0]’ call convention. Just didn’t seem to set properly via any call but the api.
  • mc.skinPercent — call is hopelessly slow and should never be used for intensive work. A query loop went from 78 seconds to run to 1.3 simply using an api weights data call even with having to re-parse the weights data to a usable format.
  • weights — speaking of, this was an obtuse concept to me. This is regards to the doubleArray list used with  an MFnSkinCluster. In short the easist way to get to a spot in this data set is as follows:
    • weights.set(value, vertIdx*numInfluences+jointIdx)
    • weights — doubleArray list instance
    • value is the given value you want
    • vertex index * the number of incluences + the joint index = the index in the array
  • Normalizing skin data — You usually want your skin values to add up to 1.0, so here’s a chunk to help
L = [.2, .5]#...list of values
normalizeTo = 1.0#...value to normalize the sum to 
[float(i)/normalizeTo for i in [float(i2)/sum(L) for i2 in L]]
#...thanks to

The initial list or requirements for the functions were as follows:

  • Readable data format — decided on configobj having used it with some red9 stuff and finding it easy to use.
  • Export/import data sets
  • Work completely from the data file for reference (no source skin necessary)
  • Work with different vertex counts if similar shape
  • Used indexed data sets for easy remapping of influences

With that being said. Here’s the demo file link and you’ll need the latest cgm package to follow along. Open up a python tab in the script editor and try these things one line at a time.

import cgm.core.lib.skinDat as SKIN
import maya.cmds as mc
#select pSphere1 then try again
d1 = we have a validated source and throw it to a variable will show some of what we have stored. No config file yet so there's not a ton there.

#...let's write our skin data to a file
d1.write()#...this will 1) gather the skinning data and 2) write it to a file where you specify more info now...
As you can see if you peruse we're storing a lot of extra data.
The idea here is to store enough that we can do some much neater
stuff down the line.

d1.validateTargetMesh('pSphere2')#...let's add a target mesh to our data object

Before we apply our data let's talk about a couple of modes we have available
:target - Uses existing target objects skin cluster influences
:source - Uses source mesh's skin cluster influences
:config - Uses config files joint names
:list - Uses a list of joints (must match config data set len and be indexed how you want it mapped)

In this case, we have no skinCluster on our object yet so source is 
d1.applySkin(influenceMode = 'source') go as we don't have a source yet
#...what about for different vert counts
d1.validateTargetMesh('pSphere_moreverts')#...let's add a new target mesh 
d1.applySkin(influenceMode = 'source') go as we don't have a source yet

Say we wanna map our data to new joints...
mc.delete('pSphere2_skinCluster')#...cause I don't have influcne changing for existing clusters working yet
newJointList = [u'joint4', u'joint4|joint2', u'joint4|joint2|joint3']
d1.validateTargetMesh('pSphere2')#...let's add a target mesh to our data object

d1.applySkin(influenceMode = 'list')#...oops
d1.applySkin(influenceMode = 'list', jointList = newJointList)#...there we go

#...other calls a file
d1.updateSourceSkinData()#...this is a call to update the source data with new weighting should you change it
SKIN.gather_skinning_dict('pSphere1')#...the data gatherer

This is a first pass on this thing till Morphy 2 is done.


Mon, Oct 19th, 2015
posted by jjburton 12:10 PM

So for Morpheus 2, the customization asset uses a blendshape head that needed to be connected to a unified body for the setup to work right. To do that I wanted to do wrap on a blendshape going into the unified body geo blendshape setup. However, we only wanted the head verts affected. If you’re not doing a procedural setup, you can simply paint out the other verts on the blendshape’s channel. Looking into a procedural solution required finding a few code chunks.

Googling on this stuff produced few helpful results so hopefully this can help someone else should they find themselves looking into these things.

First chunk we needed was the setAttr command on setting specific blendshape target weights. This one was a hard one to track down for whatever reason but here it is:


The components of this chunk are as follows:

  • unifiedBridge_blendshape — this is the blendshape node
  • inputTarget[0] — this is the index of the shape on the blendshape node we want
  • inputTargetGroup[1] –As best I understand it this is the target information for 1, or when the shape is fully on
  • targetWeights[563] — this is the vertex id of the vertice in question
  • 1.0 — value to set

As I had a head mesh that was gonna deform that same head that had been unified with the body to have a single mesh for easier skinning/shaping use, I needed the verts I wanted to mask on (for the head verts) and off (for the rest). So first all the verts are turned off, then the verts we wanted turned back on are done so with something like this:

for vtx in range(0,mc.polyEvaluate('M1_Head_geo',v=True)):#...for each vertice of the head geo
    pos = distance.returnWorldSpacePosition('M1_Head_geo.vtx[{0}]'.format(vtx))#...grab the position with our use of mc.pointPosition is the cgm.lib.distance module
    val = distance.returnClosestPointOnMeshInfoFromPos(pos,'M1_UnifiedBase_GEO')['closestVertexIndex']#...get the closest vertice id from the unified mesh using our use of maya point on surface stuff. Again in cgm.lib.distance
    mc.setAttr('unifiedBridge_blendShape.inputTarget[0].inputTargetGroup[1].targetWeights[{0}]'.format(val),1.0)#...then our vertice value is plugged in to the set attr chain

This is utilized with some modification during the setup process for a customization asset.

Have a great day!


Mon, Mar 2nd, 2015
posted by jjburton 01:03 PM

So,  a chat with a good buddy (Scott Englert) on the topic brought up one more item I’d neglected to rule out in the previous bit of research.

Flush prefs.


So I did and the issue vanished, which led me to delve into what script or preference was causing issues. Did this by re-adding files and scripts to figure out what it was. As it happens it wasn’t a specific file but an import that did it. In the end, I found 2 issues at play – one of which is solved but broke my unit tests so I’m waiting to for Red9 to figure out how to resolve. In this case it was registering ‘transform’ as a nodeType in r9Meta stuff in one of my __init__ files.

r9Meta.registerMClassNodeMapping(nodeTypes = ['transform'])#...stupid line of code!

Hopefully finding this will make the red9 stuff which we love all the better once it’s resolved and keep others from bounding into the time sucking vortex that resulted.

Lesson learned – if something is acting weird, clear prefs and see if it fixes it and if it does, start adding stuff back till you find the culprit.


Sun, Feb 15th, 2015
posted by jjburton 01:02 PM

While doing some optimization on Morpheus 2 and incorporating some of Red9‘s latest stuff I noticed an odd bottleneck in the code.  So I decided to dig in to it.

For those short of time, the short of it:

  • Maya’s duplicate command get’s slower based on scene complexity – regardless of manually calling or doing it through the interface
  • Maya’s duplicate command (and perhaps others) get slower based on how long you’ve been doing stuff in maya

I noticed that a relatively simple step in one of my joint chain functions was oddly slow and delved into it. In a empty or lite scene it was pretty instantaneous but in a regular scene got really slow. Dug in and came to just being duplicate being slow. That was my theory at least , so I wrote series of tests to verify. The first of those being a test that given a number of times to iterate and a number of children joints, the test will 1) create a joint chain of y joints and 2) duplicate that root joint iterating on the provided number. 

The results showed a pretty linear line of increasing speed as the scene added more objects. The more objects, the longer things got. Interesting but not enough to go on.

The second series of tests I wrote my own simple joint duplicate function using mc.joint and matching positioning, rotateOrder etc. I also checked some other items to eliminate those as possible hindrances or see if they affected speeds:

  • Undo – No difference whether it is on or off
  • History – No history on joints
  • Connections — Only connection on any tested joints in inverseScale
  • Flags — Tried all combinations on the duplicate command I could think of to no avail
My rewrite is always the same speed regardless of complexity, mc. duplicate get’s progressively slower as it goes on. Here are some results:
 speedTest_duplicateInPlace >> Iterations: 200 | Breakpoint Step: 24
 speedTest_duplicateInPlace >> Method 1 | Start: 0.015 | End: 0.192 | Difference: 0.177 | Total: 20.455 
 speedTest_duplicateInPlace >> Method 2 | Start: 0.037 | End: 0.036 | Difference: -0.001 | Total: 7.192 
 speedTest_duplicateInPlace >> Compare  |   Dif: 0.022 | Dif: -0.155 |                    Total: -13.264 
Maya: 2011 x64 | OS: Microsoft Windows 7 Business Edition, 64-bit Windows 7 Service Pack 1 (Build 7601)

 speedTest_duplicateInPlace >> Iterations: 200 | Breakpoint Step: 55
 speedTest_duplicateInPlace >> Method 1 | Start: 0.010 | End: 0.120 | Difference: 0.110 | Total: 13.098 
 speedTest_duplicateInPlace >> Method 2 | Start: 0.042 | End: 0.040 | Difference: -0.002 | Total: 8.160 
 speedTest_duplicateInPlace >> Compare  |   Dif: 0.032 | Dif: -0.080 |                    Total: -4.938 
Maya: 2014 x64 | OS: Microsoft Windows 7 Business Edition, 64-bit Windows 7 Service Pack 1 (Build 7601)

Breakpoint is the iteration at which my rewrite is faster than mc.duplicate for that run. Method 1 is mc.duplicate and Method 2 is my own.

How about some code you can play with yourself with a simple locator duplication?

def speedTest_simpleLocator(iterations = 100):
    import maya.cmds as mc
    import maya.mel as mel
    import time

    _loc = mc.spaceLocator()[0]
    l_times = []

    for i in range(iterations):

	t1 = time.clock()		
	mc.duplicate(_loc, po = True, ic = False, un = False)
	t2 = time.clock()

    for i,t in enumerate(l_times):
	print("Step {0} |  {1}".format(i,"%0.3f"%t))

    _str_dif = l_times[-1] - l_times[0] 
    print(" CGM Simple loc duplication -- {0} | Start -- {1} | End -- {2} | [Diff] -- {3} ".format("%0.3f"%(sum(l_times)),"%0.3f"%l_times[0],"%0.3f"%l_times[-1],"%0.3f"%_str_dif))
    print(" Maya: {0} | OS: {1}".format(mel.eval( 'about -%s'%'version'), mel.eval( 'about -%s'%'operatingSystemVersion')))

Here are some results of this standalone for me at least:

 CGM Simple loc duplication -- 88.324 | Start -- 0.007 | End -- 0.334 | [Diff] -- 0.327 
 Maya: 2011 x64 | OS: Microsoft Windows 7 Business Edition, 64-bit Windows 7 Service Pack 1 (Build 7601)

 CGM Simple loc duplication -- 59.259 | Start -- 0.005 | End -- 0.216 | [Diff] -- 0.211 
 Maya: 2014 x64 | OS: Microsoft Windows 7 Business Edition, 64-bit Windows 7 Service Pack 1 (Build 7601)

 CGM Simple loc duplication -- 40.160 | Start -- 0.004 | End -- 0.151 | [Diff] -- 0.147 
 Maya: 2015 | OS: Microsoft Windows 7 Business Edition, 64-bit Windows 7 Service Pack 1 (Build 7601)

Note – the 2015 run was a fresh open of the software and my experience doing this testing would see that getting slower.

If you run the test you’ll see for yourself the slow down. Now, what do we do with this?Working through this I created my own duplicator for curves, joints and locators. For now, I’m only going to use the joint one for Morpheus building until I can do some more testing and maybe get a better handle on it but it’s certainly an oddity.

Odder yet is the longer you do stuff in Maya, duplicate gets slower still. This I tested after noticing that after being away a bit that Windows had rebooted and suddenly duplicate was posting much better results. After a while, that slowed down. So I rebooted myself and yup, it’s faster after a reboot.  I have no idea on this one other than maybe a memory leak or..I dunno, I’m a hack at this stuff and that’s the best I got:)

If you’re interested, let me know what you find – different conclusions?

For now, Bokser told me I have to move on:)

Thu, Dec 26th, 2013
posted by jjburton 03:12 PM

We have partnered with Rigging Dojo on creating special project “Rigging for Morpheus” that aims to cover learning how to integrate the red9 tools, how to work with an existing open source library and with set standards so that students can build tools, expand on/improve and contribute to the opensource CGMonks Took kit as well as the Morpheus rig project.

This isn’t a class to learn to use Morpheus – that will be handled via some vids at release. This is for those td’s who wanna join in in making as great a shared tool set as possible.

Class is kicking off next month. Registration can be found here:

All registrations must be in by 31 December, 2013

Studio Portion
The specifics of what that studio session will cover will be subject to what the ‘students’ want to do. If everyone wants to work on customization stuff, then we’ll work on that together, if one student has a great idea for a tool and just needs help learning how to build that and integrate it to an existing ‘pipeline’, we’d support that student on that and maybe other folks on something else.

Potential Paths

  1. Customization — Work through building guts and tech for the finalizing the Morpheus 2 customization setup. This would be tackling such exciting items as – blendshape libraries, hair, prop clothing implementation. Definitely a beard. Morpheus needs a beard.
  2. New module — Build a new module for the system like a wing, long neck head, quad leg or perhaps some more mechanical setups. This would entail designing and implementing template objects and learning to write and push that through to a final rig item that ties in to the rest of the modular rigger.
  3. Facial system — Work through learning the new methods and tools of the Morpheus 2 facial rig. Polishing, adding features and learning how to build on it.
  4. New/Existing Tool Pipeline — Learn the bits of designing and writing a new tool for maya – from gui, designing for user friendliness, breaking down the tool to build-able bits, debugging and more. If students have tool designs we can start from scratch or we can grab from the ever growing pile of internal ideas from CG Monks.

Class (8 week intensive apprenticeship)
$1226 – base cost
There are discounts available for Morpheus 2 backers. See the development forum post for  more info.

Tue, Sep 17th, 2013
posted by jjburton 10:09 AM

First off, sorry for the big break in posts here. We’ve just been swamped working on Morpheus and haven’t posted much. This sounded like a good topic.

As we’re getting close to finishing up Morpheus 2, I’ve been trying to do some optimization in the code to make the build processes faster and other things. One of the questions I had was if log.debug was causing speed hits and what other affects prints or log.infos were having on our main code speeds.

What does one do when trying to solve something? Well, we make some hypotheses and tests for those hypotheses. Go, go 8th grade science!

So I wrote a test this morning and the results are rather interesting. I wrote a series of timed functions that do a pow calculation on an enumerated range. I gave the tester a single kw arg of setting the maxTest which sets the range end. I’ll post the tester code below.

The results on a 3000 range test are as follows:

  • logger >> Time >> = 46.289 seconds
  • debug >> Time >> = 24.752 seconds
  • if debug >> Time >> = 0.000 seconds
  • print >> Time >> = 150.508 seconds
  • calc >> Time >> = 0.569 seconds

The raw calc is obviously the fastest.  What was curious to me was that debugs take a hit even when not printing. What was most crazy was how slow print statements are and concluded that you should pretty much never ever use them in maya tools.

For the purposes of our own debugging, using an if/debug gate on that info seemed like it was going to make the most sense. So with that in mind I tried a way to find the level of the logger. The only call I could initially find was log.getEffectiveLevel(). That seems to be the most intuitive method to get a if/debug check for free. Need to do more research to figure out if a level of 10 – which seems to be debug’s default integer value is a usable check.

On a whim I pulled all the log.debugs from our core code and the time savings was considerable on our unit tests. Blrgh…yet another item for the ever growing to do pile.

In the end these are my initial conclusions:

  • Never use ‘print’ calls in maya python. Use logger –
  • Only raw log.debugs in when no calculations have to happen to form that report. I.e – they’ve already been done.
  • I need to begin working to pull out as many debugs as possible and/or level-gate those calls for speed’s sake.
import time
import logging
log = logging.getLogger(__name__)

def testDebugCalls(maxTest = 500):
    ifDebug = False
    def logTest():
        _str_funcName = 'logger'
        time_start = time.clock()
        for i,n in enumerate(range(1,maxTest)):
  "log >>> %s"%(pow(n,i)))
        return "%s >> Time >> = %0.3f seconds " % (_str_funcName,(time.clock()-time_start))
    def debugTest():
        _str_funcName = 'debug'
        time_start = time.clock()
        for i,n in enumerate(range(1,maxTest)):
            log.debug("debug >>> %s"%(pow(n,i)))
        return "%s >> Time >> = %0.3f seconds " % (_str_funcName,(time.clock()-time_start))
    def ifDebugTest():
        _str_funcName = 'if debug'
        time_start = time.clock()
        for i,n in enumerate(range(1,maxTest)):
            if log.getEffectiveLevel() == 10:log.debug("debug EFFECTIVELEVEL >>> %s"%(pow(n,i)))
        return "%s >> Time >> = %0.3f seconds " % (_str_funcName,(time.clock()-time_start))
    def printTest():
        _str_funcName = 'print'
        time_start = time.clock()
        for i,n in enumerate(range(1,maxTest)):
            print("print >>> %s"%(pow(n,i)))
        return "%s >> Time >> = %0.3f seconds " % (_str_funcName,(time.clock()-time_start))  
    def calcTest():
        _str_funcName = 'calc'
        time_start = time.clock()
        for i,n in enumerate(range(1,maxTest)):
            x = (pow(n,i))
        return "%s >> Time >> = %0.3f seconds " % (_str_funcName,(time.clock()-time_start))

    time_log = logTest()
    time_debug = debugTest()
    time_ifDebug = ifDebugTest()    
    time_print = printTest()
    time_calc = calcTest()

    for l in [time_log,time_debug,time_ifDebug,time_print,time_calc]:
        print l
Wed, Jan 2nd, 2013
posted by jjburton 02:01 PM

Hey Morphean stylists! Ever walked out of the barbershop not pleased with your cut and was too shy to admit it? WELL THEN! Hide no more dear friends! For tonight we dine in HELL! (sorry wrong quote) For tonight we control of the scissors! *cue explosions* That’s right! Time to put the pens and pencils on the paper (or fancy tablets) and style our Morpheus up with a fresh coiffure! That’ll teach that barber! *cue more explosions*

Morphy2HairTemplate_male Morphy2HairTemplate_female


  • Try to draw the hair in its neutral position (not in movement).
  • If your hair style is somewhat complexe please provide some general notes. You can scribble those on your template (dont overlap the drawing too much).
  • The styles will be split into 3 categories: Short (above shoulders), Medium (shoulder level) & Long (below shoulders) so please try your best to be clear on the length.
  • You don’t have to submit sparkling clean designs. Clear sketches are more than welcome.
  • No submission limits.

Template files


  • Submit in PNG or JPEG.
  • Naming convention: YourName_HairCustomize_v00.ext (the versioning is not for WIP numbers but for your designs).
  • Submition deadline – January 24th at midnight.

To submit your work, you can send it to my email: alexei.bresker (->at<-)…sorry, for the spam bots inconvenience.
(subject: Morpheus Rig 2.0 – Hair Styles)

-Alexei (Morpheus 2.0 Design Producer)

Mon, Nov 12th, 2012
posted by jjburton 11:11 AM

Thank you, animation community!

Thu, Nov 1st, 2012
posted by jjburton 03:11 PM

We’re trying out the name ProblemKicker. If anyone else had a better one, we’re all ears.

Mon, Oct 29th, 2012
posted by jjburton 11:10 AM

Some bug fixes and a few new features in this build. The biggest but was Locinator’s locator connections breaking on file reload to referenced object.


Added a standalone buttonable call for locinator’s update function at a user request. If you wanted to use that, use this as your button:
[crayon]from import locinatorLib

  • General
    • started playing with debug modes on tools
  • cgm.guiFactory
    • removed Bridge call from a failed experiment. Causing errors
  • attrTools/attrToolsLib
    • Added debug option
  • DraggerContextFactory
    • Removed joint sizing at tester request till we find a more dependable sizing logic function
  • setKeyMM
    • added breakdown/reg options
    • added a new keying mode which prioritizes selected channel box channes first, then regular selection
  • AttrFactory
    • Added message repairing for referenced objects connected to a message attr
  • Attributes
    • (new) repairMessageToReferencedTarget(obj,attr) – function to repair broken message connections to referenced objects
  • polyUnite
    • added version to window
  • animTools
    • added version to window
    • added debug flags
  • puppetBox
    • added version
    • added debug flags
  • polyUnite(standalone)
    • added version to window
  • search
    • reuturnTagInfo – updated to attempt to repair broken connections to referenced objects for message attributes only
  • locinator/locinatorLib
    • added debug
    • added version to window
    • (new) – doStandAloneUpdateSelected() – added standalone call for users to use a button to run update object
    • moved mode toggle to options menu to allow it to easily share the setting with other tools