Planet Tech Art
Last update: October 26, 2014 05:00 AM
October 25, 2014

Nexus Distributed Rendering plugin

Today is a great day! Finally I can share with you all what I’ve been working on along with my good friend Daniel Santana and Jonathan de Blok! Nexus DBR is a plugin available for 3ds Max  users based on the Nexus Framework.

Nexus DBR plugin allows fast iterations on renders without the hassle of network rendering while using your render farm in an interactive workflow. Some of its main features include:

  • Easy scene file distribution
  • Works with any commercially available renderer
  • Does not block the application so you can work while rendering, no extra overhead

But words are just words! So here’s a video showing it all :) Hope you like it and if you are interested in knowing more about price and licensing scheme contact us at info@youcandoitvfx.com

Click here to view the embedded video.

by Artur Leao at October 25, 2014 07:03 PM


October 24, 2014

Another one bites the dust. A bunch of #inktober in this one



Another one bites the dust. A bunch of #inktober in this one

by at October 24, 2014 11:21 PM


October 23, 2014

Technical debt takes many forms

Most people are familiar with “technical debt” in terms of code or architectural problems that slow down development. There are other forms of technical debt, though, that can be overlooked.

Dead Code: There are endless “dead code as debt” scenarios. You have a “live” function that is only used from dead code, hiding the fact that this function is also dead (this situation is cancerous). Every time you grep, you have to wade through code that is dead. Every time someone stumbles across the dead code, they need to figure out if it’s dead or alive. There’s no reason for any of this (especially not “keep it around as reference”). Dead code is a debt, but it’s also easy to pay back. Remove your dead code.

Unused Repos or Branches: Every time a new person starts, they will ponder what code is dead and what is alive. This pondering includes code, issues, and documentation. It is sloppy and unnecessary. Put unused repositories in cold storage. Delete stale branches.

Large Backlog: The larger the backlog, the worse the experience of using it. It’s harder to find, reclassify, and prioritize. Some developers will not even bother. A backlog is not a place for everyone to list anything they think should ever be done. Close stale and low-priority tickets. Close “symptom” tickets that you know won’t be addressed until a system is rewritten. Close everything except 3 months of work (and manage further out work on your roadmap, which should not be in your backlog).

Dirty Wikis/Documentation: Why out of date documentation is harmful should be pretty self-explanatory. Don’t let it get that way (or just delete the documentation). Make documentation someone’s responsibility, or make it part of the “definition of done.”

Every organization has these things. By recognizing them as debt, and thus detrimental to development, it can perhaps simplify any argument about what to do.

by Rob Galanakis at October 23, 2014 12:55 PM


October 22, 2014

Ramahan Faulk New Mentor, New Course at Rigging Dojo

We are excited and happy to welcome and old friend, Ramahan Faulk, recently of Digital Domain, to Rigging Dojo as our new modeling Mentor! Ramahan will be teaching a course …

The post Ramahan Faulk New Mentor, New Course at Rigging Dojo appeared first on Rigging Dojo.

by Rigging Dojo at October 22, 2014 03:35 PM


October 19, 2014

PracticalMayaPython: RuntimeError: Internal C++ object (PySide.QtGui.QStatusBar) already deleted.

TLDR: If you get that error for the code on page 163, see the fix at https://github.com/rgalanakis/practicalmayapython/pull/2/files

In August, reader Ric Williams noted:

I’m running Maya 2015 with Windows 7 64-bit. On page 163 when we open the GUI using a Shelf button, the GUI status bar does not work, (it works when called from outside Maya). This RuntimeError appears: RuntimeError: Internal C++ object (PySide.QtGui.QStatusBar) already deleted.

I no longer have Maya installed so I couldn’t debug it, but reader ragingViking (sorry, don’t know your real name!) contributed a fix to the book’s GitHub repository. You can see the fix here: https://github.com/rgalanakis/practicalmayapython/pull/2/files
And you can see the issue which has more explanation here: https://github.com/rgalanakis/practicalmayapython/issues/1

Thanks again to Ric and ragingViking. I did my best to test the code with various versions of Maya but definitely missed some things (especially those which required manual testing). If you find any other problems, please don’t hesitate to send me an email!

by Rob Galanakis at October 19, 2014 05:30 PM


October 17, 2014

Embedding Icons and Images In Python with XPM

xpm1

As technically-inclined artists, we often like to create polished UIs, but we have to balance this with not wanting to complicate the user experience (fewer files the better). I tend to not use too many icons with my tools, and my Maya tools often just steal existing icons from Maya: because I know they will be there.

However, you can embed bitmap icons into your PySide/PyQt apps by using the XPM file format, and storing it as a string array. Often I will just place images at the bottom of the file, this keeps icons inside your actual tool, and you don’t need to distribute multiple files or link to external resources.

Here’s an example XPM file:

/* XPM */
static char *heart_xpm[] = {
/* width height num_colors chars_per_pixel */
"7 6 2 1",
/* colors */
"` c None",
". c #e2385a",
/* pixels */
"`..`..`",
".......",
".......",
"`.....`",
"``...``",
"```.```"
};

This is a small heart, you can actually see it, in the header you se the ‘.’ maps to pink, you can see the ‘.’ pattern of a heart. The XPM format is like C, the comments tell you what each block does.
Here’s an example in PySide that generates the above button with heart icon:

import sys
from PySide import QtGui, QtCore
 
def main():
    app = QtGui.QApplication(sys.argv)
    heartXPM = ['7 6 2 1','N c None','. c #e2385a','N..N..N',\
    '.......','.......','N.....N','NN...NN','NNN.NNN']
    w = QtGui.QWidget()
    w.setWindowTitle('XPM Test')
    w.button = QtGui.QPushButton('XPM Bitmaps', w)
    w.button.setIcon(QtGui.QIcon(QtGui.QPixmap(heartXPM)))
    w.button.setIconSize(QtCore.QSize(24,24))
    w.show()
 
    sys.exit(app.exec_())
 
if __name__ == '__main__':
    main()

You need to feed QT a string array, and strip everything out. Gimp can save XPM, but you can also load an image into xnView and save as XPM.

Addendum: Robert pointed out in the comments that pyrcc4, a tool that ships with PyQt4, can compile .qrc files into python files that can be imported. I haven’t tried, but if they can be imported, and are .py files, I guess they can be embedded as well. He also mentioned base64 encoding bitmap images into strings and parsing them. Both these solutions could really make your files much larger than XPM though.

The post Embedding Icons and Images In Python with XPM appeared first on Stumbling Toward 'Awesomeness'.

by admin at October 17, 2014 01:23 PM


October 16, 2014

Tracing and Visualizing Driven-Key Relationships

sdk

Before I get into the collosal mess that is setting driven keys in Maya, let me start off by saying, when I first made an ‘SDK’ in college, back in 1999, never did I think I would still be rigging like this 15 years later. (Nor did I know that I was setting a ‘driven key’, or a ‘DK’ which sounds less glamorous)

How The Mess is Made

simple

Grab this sample scene [driven_test]. In the file, a single locator with two attributes drives the translation and rotation of two other locators. Can’t get much ismpler than that, but look at this spaghetti mess above! This simple driven relationship created 24 curves, 12 blendWeighted nodes, and 18 unitConversion nodes. So let’s start to take apart this mess. When you set a driven-key relationship, it uses an input and a curve to drive another attribute:

curves2

When you have multiple attributes driving a node, maya creates ‘blendWeighted’ nodes, this blends the driven inputs to one scalar output, as you see with the translateX below:

curves

Blending scalars is fairly straight forward, but for rotations, get ready for this craziness: A blendWeighted node cannot take the output of an animCurveUA (angle output), the value must first be converted to radians, then blended. But the final node cannot take radians, so the result must be converted back to an euler angle. This happens for each channel.

craziness

If you think this is retarded; welcome to the club. It is a very cool usage of general purpose nodes in Maya, but you wouldn’t think so much of rigging was built on that, would you? That when you create a driven-key it basically makes a bunch of nodes and doesn’t track an actual relationship, because of this, you can’t even reload a relationship into the SDK tool later to edit! (unless you traverse the spaghetti or takes notes or something)

I am in love with Node Edtor, but by default hypergraph did hide some of the spaghetti, it used to hide unitConversions as ‘auxiliary nodes’:

auxnode

Node Editor shows unitConversions regardless of whether aux nodes are enabled, I would rather see too much and know what’s going on than have things hidden, but maybe that’s just me. You can actually define what nodes are considered aux nodes and whether ‘editors’ show them, but I am way off on a tangent here.

So just go in there and delete the unit conversion yourself and wire the euler angle output, which you would think is a float.. into the blendWeighted input, which takes floats. Maya won’t let you, it creates the unitConversion for you because animCurveUA outputs angles, not floats.

This is why our very simple example file generated over 50 nodes. On Ryse, Maruis’ face has about 73,000 nodes of driven-key spaghetti. (47,382 curves,  1,420 blendWeighted, 24,074 unitConversion)

Finding and traversing

So how do we find this stuff and maybe query and deal with it? Does Maya have some built in way of querying a driven relationship? No. Not that I have ever found. You must become one with the spaghetti! setDrivenKeyframe has a query mode, but that only tells you what ‘driver’ or ‘driven’ nodes/attrs are in the SDK window if it’s open, they don’t query driver or driven relationships!

We know that these intermediate nodes that store the keys are curves, but they’re a special kind of curve, one that doesn’t take time as an input. Here’s how to search for those:

print len(cmds.ls(type=("animCurveUL","animCurveUU","animCurveUA","animCurveUT")))

So what are these nodes? I mentioned above that animCurveUA puts out an angle:

  • animCurveUU – curve that takes a double precision float and has a double output
  • animCurveUA – takes a double and outputs an angle
  • animCurveUL – takes a double and outputs a distance (length)
  • animCurveUT – takes a double and outputs a time

When working with lots of driven-key relationships you frequently want to know what is driving what, and this can be very difficult because of all the intermediate node-spaghetti. Here’s what you often want to know:

  • What attribute is driving what – for instance, select all nodes an attr drives, so that you can add them back to the SDK tool. ugh.
  • What is being driven by an attribute

I wrote a small script/hack to query these relationships, you can grab it here [drivenKeyVisualizer]. Seriously, this is just a UI front end to a 100 line script snippet, don’t let the awesomeness of QT fool you.

dkv1

The way I decided to go about it was:

  1. Find the driven-key curves
  2. Create a tiny curve class to store little ‘sdk’ objects
  3. List incoming connections (listConnections) to get the driving attr
  4. Get the future DG history as a list and reverse it (listHistory(future=1).reverse())
  5. Walk the reversed history until I hit a unitConversion or blendWeighted node
  6. Get it’s outgoing connection (listConnections) to find the plug that it’s driving
  7. Store all this as my sdk objects
  8. Loop across all my objects and generate the QTreeWidget

Here’s how I traversed that future history (in the file above):

 #search down the dag for all future nodes
 futureNodes = [node for node in cmds.listHistory(c, future=1, ac=1)]
 #reverse the list order so that you get farthest first
 futureNodes.reverse()
 drivenAttr = None
 
 #walk the list until you find either a unitConversion, blendWeighted, or nothing
 for node in futureNodes:
     if cmds.nodeType(node) == 'unitConversion':
         try:
             drivenAttr = cmds.listConnections(node + '.output', p=1)[0]
             break
         except:
             cmds.warning('blendWeighted node with no output: ' + node)
             break
     elif cmds.nodeType(node) == 'blendWeighted':
         try:
             drivenAttr = cmds.listConnections(node + '.output', p=1)[0]
             break
         except:
             cmds.warning('blendWeighted node with no output: ' + node)
             break
 if not drivenAttr:
     drivenAttr = cmds.listConnections(c + '.output', p=1)[0]
 if drivenAttr:
     dk.drivenAttr = drivenAttr
 else:
     cmds.warning('No driven attr found for ' + c)

This of course won’t work if you have anything like ‘contextual rigging’ that checks the value of an attr and then uses it to blend between two banks of driven-keys, but because this is all general DG nodes, as soon as you enter nodes by hand, it’s no longer really a vanilla driven-key relationship that’s been set.

If you have a better idea, let me know, this above is just a way I have done it that has been robust, but again, I mainly drive transforms.

 What can you do?

Prune Unused Pasta

pruned

Pruned version of the driven_test.ma DAG

By definition, when rigging something with many driven transforms like a face, you are creating driven-key relationships based on what you MIGHT need. This goes for when making the initial relationship, or in the future when you maybe want to add detail. WILL I NEED to maybe translate an eyelid xform to get a driven pose I want.. MAYBE.. so you find yourself keying rot/trans on *EVERYTHING*. That’s what I did in my example, and the way the Maya SDK tool works, you can’t really choose which attrs per driven node you want to drive, so best to ‘go hunting with a shotgun’ as we say. (shoot all the trees and hope something falls out)

Ok so let’s write some code to identify and prune driven relationships we didn’t use.

CAUTION: I would only ever do this in a ‘publish’ step, where you take your final rig and delete crap to make it faster (or break it) for animators. Also, I have never used this exact code below in production, I just created it while making this blog post as an example. I have run it on some of our production rigs and haven’t noticed anything terrible, but I also haven’t really looked. :D

def deleteUnusedDriverCurves():
    for driverCurve in cmds.ls(type=("animCurveUL","animCurveUU","animCurveUA","animCurveUT")):
        #delete unused driven curves
        if not [item for item in cmds.keyframe(driverCurve, valueChange=1, q=1) if abs(item) > 0.0]:
            cmds.delete(driverCurve)
 
deleteUnusedDriverCurves()

This deletes any curves that do not have a change in value. You could have multiple keys, but if there’s no curve, let’s get rid of it. Now that we have deleted some curves, we have some blendWeighted nodes that now aren’t blending anything and unitConversions that are worthless creatures. Let’s take care of them:

def deleteUnusedBlendNodes():
    #rewire blend nodes that aren't blending anything
    for blendNode in cmds.ls(type='blendWeighted'):
        if len(cmds.listConnections(blendNode, destination=0)) == 1:
            curveOutput = None
            drivenInput = None
 
            #leap over the unitConversion if it exists and find the driving curve
            curveOutput = cmds.listConnections(blendNode, destination=0, plugs=1, skipConversionNodes=1)[0]
            #leap over the downstream unitConversion if it exists
            conns = cmds.listConnections(blendNode, source=0, plugs=1, skipConversionNodes=1)
            for conn in conns:
                if cmds.nodeType(conn.split('.')[0]) == 'hyperLayout': conns.pop(conns.index(conn))
            if len(conns) == 1:
                drivenInput = conns[0]
            else:
                cmds.warning('BlendWeighted had more than two outputs? Node: ' + blendNode)
 
            #connect the values, delete the blendWeighted
            cmds.connectAttr(curveOutput, drivenInput, force=1)
            cmds.delete(blendNode)
            print 'Removed', blendNode, 'and wired', curveOutput, 'to', drivenInput
 
deleteUnusedBlendNodes()

We find all blendWeighted nodes with only one input, then traverse out from them and directly connect whatever it was the node was blending, then we delete it. This is a bit tricky and I still think I may have missed something because I wrote this example at 2am, but I ran it on some rigs and haven’t seen an issue.

Here are the results running this code on an example rig:

pruned_graph

Create a Tool To Track / Mirror / Select Driven Relationships

This is a prototype I was working on at home but shelved, I would like to pick it up again when I have some time, or would be open to tossing it on github if people would like to help. It’s not rocket science, it’s besically what Maya should have by default. You just want to track the relationships you make, and also select all nodes driven by an attr. Also mirror their transforms across an axis (more geared toward driven transforms).

sdkWrangler

Write Your Own Driver Node

Many places create their own ‘driven node’ that just stores driven relationships. Judd Simantov showed a Maya node that was used on Uncharted2 to store all facial poses/driven relationships:

facePoseNode

The benefits of making your own node are not just in DAG readability, check out the time spent evaluating all these unitConversion and blendWeighted nodes in a complex facial rig (using the new Maya 2015 EXT 1 Profiler tool) –that’s over 760ms! (click to enlarge)

profiler

Though it’s not enough to make a node like this, you need to make a front end to manage it, here’s the front end for this node:

poseFaceUI

Give Autodesk Feedback!

feedback

As the PSD request is ‘under review’, maybe when they make the driver, they can make a more general framework to drive things in Maya.

Conclusion

As you can see, there are many ways to try to manage and contain the mess generated by creating driven-key relationships.  I would like to update this based on feedback, it’s definitely not an overview, and speaks more to people who have been creating driven-key relationships for quite some time.  Also, if you would find a tool like my Maya SDK replacement useful, let me know, especially if you would like to help iron it out and/or test it.

The post Tracing and Visualizing Driven-Key Relationships appeared first on Stumbling Toward 'Awesomeness'.

by admin at October 16, 2014 01:16 PM


October 15, 2014

Reminder: Maya saves ScriptEditor tabs on crash

backup

This is just a reminder that for some time now Maya has been saving all ScriptEditor tabs on crash. I frequently bump into people who don’t know or don’t remember this. If your Maya says that it’s attempting to save in your /Temp folder, it’s also saving all your ScriptEditor tabs.

The post Reminder: Maya saves ScriptEditor tabs on crash appeared first on Stumbling Toward 'Awesomeness'.

by admin at October 15, 2014 09:56 AM


October 12, 2014