I find that a lot of people write about a final outcome, or maybe a thought, so this time I figured I’d look at a process that I went through today, which isn’t dissimilar to processes that I go through everyday to make tasks in my life simpler. I just thought this was a good example of one idea being worked on and then presented to an outsider and seeing how we can make a working solution better.
During my typical workflow I try to break a big project into several smaller tasks to simplify complicated jobs. While breaking down a task today I ran into another issue – the time it takes to complete that task. Here’s the breakdown: I have 50 blend shapes with very complicated parts and pieces. On each of those blend shapes I need to grab the same vertices every time, then manipulate the verts. For the first round I decide that I will make my selection and save it as a ‘SET’ (Quick Selection Set) so that I have all the data available later.
The beauty of blendshapes is that you have to have the same vert count which means they have the same vert ID too. So with that in mind your verts end up with a name like myVert.vtx – myVert.vtx (assuming you had 100 verts). I had around 40k+ verts in each selection. My first thought was to take the set that I had created and split apart the ‘myVert’ and ‘vtx’ from myVert.vtx, giving me the ability to add something new to the front of ‘vtx’. With the split command in python this is very simple, you can then replace ‘myVert’ with something else (like ‘otherObj’), which could also be the name of the next blendshape I want to edit. Pretty simple, right?
The code worked fine, the problem was that it took 25 mins to parse through the whole set of 40k verts, break the name apart, assign the name of the new object and then individually select each and every vert on that object.
In the background this is what is being selected in the UI:
theList = [u’myVert.vtx’, u’myVert.vtx’,…]
Because this is selected the getList variable will pick this up and assign the list to that variable.
import maya.cmds as mc getList = mc.ls(sl=True, fl=True) getObj = mc.ls(sl=True, tr=True) for obj in getList: vertNum = obj.split('.') mc.select(getObj + "." + vertNum, add=True)
Looking at the list you'll see 40k objects in it. Maya will do a clever thing (and I use that word sparingly with Maya as the user usually has to out wit the program...) - if it sees that there are long lists of values that are sequential it will concatenate them into something like this:
theList = [u'myVert.vtx[0:50]', u'myVert.vtx[51:99]']
Your result is that rather than having 100 objects in the list it now has 2.
So now you ask, why this didn't happen in the list above? It's because in the past I've had issues when I don't use the 'fl=True' flag in the ls command. For those who don't know, ls is short for List and fl is short for Flatten. What this does is look at each and every item in an array. I've had plenty of situations where an element runs through a loop and doesn't get processed correctly, so my brain has since been hard-wired to always add this flag.
Another question you might ask is what happens when you remove:
getList = mc.ls(sl=True, fl=True)
Well, the process time goes from 25 mins to 25 seconds. The list did shrink from 40k to 70 which means that it had less to iterate through in terms of physical list items. It did however still have to calculate all the values that get truncated. That means that the values between the ':' get calculated faster or differently then listed values, the answer may lie in the API or even something deeper within python. I had time to solve one issue today but unfortunately not enough to look through this one. I'll save it for a rainy day 😉
Let me back up for a second. I wouldn't have come to this conclusion had it not been for a colleague of mine - I have the luxury of working around a group of very talented developers (non 3D developers), something I'm not all that accustomed too. While explaining what was going on I stumbled across the flag, and so removed and tested it to explain what it did. As a result with such a significant time difference after taking the flag out of the code we sort of left the conversation as it stood.
With my times dropping from 25 minutes to 25 seconds I thought I would just ask someone else to see if they could come up with an even faster solution (hey, why not?). Really at this stage it was just trivial but I wanted to see if there was something a bit more elegant. As it turned out my supervisor had another view to the solution:
import maya.cmds as mc object_name = mc.ls(sl=True, tr=True) mc.select('test') verts = mc.ls(sl=True) #select the verts from list verts = mc.select('test') vertnums =  for vert in verts: tmp,v = vert.split('.') vertnums.append(object_name + '.' + v) mc.sets(*vertnums, n='newSet') mc.select('newSet') mc.delete('newSet')
I had to stop him here as he was already opening the docs for the Maya API, determined he could make it even faster. This almost does the same thing, except that rather than splitting the values apart and then selecting each vert he pushed all the new data into a new list and applied it to a set. The code selects the set and then deletes itself for it's next use. Amazingly this dropped the time to about 1 second of processing. Not a bad turn around for 30 minutes of goofing around with some code! It also proves an interesting point I commonly see online where users ask if there is a faster way to approach a problem, granted this is a simple obstacle. In my experience when you're up against a brutal deadline you just try to find a solution and use it - so even if it took me 25 minutes to make the selection I probably wouldn't go back to solve the issue because I don't know how long that would take. It's still a faster process than selecting each vert on a complex item (not to mention it takes the absolute boredom out of a mundane process!).
I guess the reason why most people don't write about the process they go through to solve problems like this is that it generally takes longer to explain the process than the evolution of the process. After working in CG for almost a decade now it's fun to look back and try and remember how or why I needed to solve these problems. It all merges into a blur with a bad diet and lack of sleep, as I'm sure anyone in this profession would agree!