Planet Tech Art
Last update: April 25, 2014 03:59 AM
April 23, 2014

Maya callbacks cheat sheet

In All Your Base Classes,  I suggested that we can do better than the standard callback mechanism for doing Maya event handling.  The limitations of the default method are something I've complained about before, and if you follow these things on TAO or CGTalk or StackOverflow it seems pretty clear that a lot of other people have problems with the standard Maya code flow too.

I was planning on devoting the next big post to the event mechanism in mGui . However as I did the spadework for this post I decided it was better to split it up into two parts, since a lot of folks seem to be confused about the right way to manage basic Maya callbacks. Before moving fancy stuff, it's a good idea to make sure the basics are clear. Most vets will already know most of what I'm going over here, but I found the  time spent laying it out for myself a useful exercise  so I figured it would be worth sharing even if it's not revolutionary.

Unsolved Mysteries of the Maya.

So, let's start by clearing up something that even a lot of old-school Maya coders find a bit mysterious when building GUIs.

In vanilla Maya, GUI components fire callbacks - that is to say that when Maya recognizes a user action like a button press or a text entry, it calls a function you've provided. There are two ways you can set this up.  The old-school MEL way is to use a string:

my_button = cmds.button('hello', command = 'print "hello"')

In the bad old days of MEL, this was usually fine since most procedures were declared as globals and so they were available everywhere.

Unfortunately, Python's stricter rules about scoping mean that you constantly run into problems with this strategy if you're not careful. For example, this straight python conversion of the Mel paradigm works fine:

def print_hello(_):
print "hello"

my_w = cmds.window()
my_col = cmds.columnLayout()
my_button = cmds.button('hello', command = "print_hello()")

But try this:

def show_test_window():
def print_hello_again(_):
print "hello"

my_w = cmds.window()
my_col = cmds.columnLayout()
my_button = cmds.button('hello', command = "print_hello_again()")

When you hit the button you'll be told

  # Error: NameError: name 'print_hello_again' is not defined #.

 That's because print_hello_again is defined in the scope of the function, not the scope of the Maya interpreter -- when the callback actually fires, the name is buried away inside of show_test_window and can't be found by Maya, at least not using the simple string name.

That "_" in the functions, by the way, is the standard python symbol for "I have to have a variable here but I intend to ignore it" - it shows up in a lot of these GUI examples because many, though not all, Maya callbacks fire off an argument when they activate

 This happens all the time to people trying to port old MEL code to Python - snippets that work in the interpreter don't work when converted to functions or split between modules because the string callbacks only execute in the global scope. Luckily, once you realize that the "where is my function" problem is just basic scoping, it's easy to fix. You can forcibly capture the functions you want by just passing them directly to your GUI callbacks instead of using strings, thanks to the magic of python's first class functions.  You just need to pass the function itself - not a quoted string that looks like the function - to the callback, Thus the previous example becomes

def show_test_window():
def print_hello_again(_):
print "hello"

my_w = cmds.window()
my_col = cmds.columnLayout()
my_button = cmds.button('hello', command = print_hello_again)
# note: no quotes and no parens. 
        # You're passing the function as an object!


Since you've got the callback in scope when you create the gui, you're certain to have it when you need it (if by some accident it was out of scope at creation time you'd get an obvious error that you'd have to fix before moving on).

Clear, predictable scoping is why it's almost always the right decision to wrap your GUIs in classes. The class defines a predictable scope so you don't  have to worry about what's loaded or try to cram import statements into your callback functions.   Plus, classes include data storage, so you can keep your data nicely independent of your code. Suppose, for example, you needed to display a different set of greetings beyond the standard "hello world."  With a class you can defer the problem up to the moment of the actual button press with no fancy footwork or complex lambda management:

class Greeter(object):
def __init__(self, greeting):
self.greeting = greeting
self.window = cmds.window()
cmds.button('hello', command = self.greet)

def show(self):

def greet(self, _):
print self.greeting

Whatever is stuffed into the Greeter's greeting field will be printed out when the button get's pressed.

Arguments for the prosecution

So, the "where the hell is my function" problem which tends to plague beginners is easy to solve once you look at it the right way.

However, right after you're comfortable with passing functions directly, you immediately realize that's not enough.  It's very common to have multiple GUI controls that do more or less the same thing with different settings such a set of buttons which make different sized objects. 

Alas, while this is easy to understand, it's also kinda ugly to code.

For starters, you might try making lots of little functions:

def Boxes():
def make_big_box(_):
cmds.polyCube(h = 10, d=10, w=10)

def make_med_box(_):
cmds.polyCube(h = 5, d=5, w=5)

def make_sm_box(_):
cmds.polyCube(h = 2, d=2, w=2)

my_w = cmds.window()
cmds.button("small box", c = make_sm_box)
cmds.button("medium box", c = make_med_box)
cmds.button("large box", c = make_big_box)

Or you could do basically the same thing using lambdas to create temporary functions, which saves on the extra defs but tends to be illegible and tough to debug for complex commands :

def BoxLambdas():
my_w = cmds.window()
cmds.button("small box", c = lambda _: cmds.polyCube(d =2, w= 2 , h=2) )
cmds.button("medium box", c = lambda _: cmds.polyCube(d = 10 , w = 5 , h = 5) )
cmds.button("large box", c = lambda _: cmds.polyCube(d = 10 , w = 10 , h = 10) )
BTW There's that underscore again, in the lambdas, doing the same job: ignoring the callback argument from the buttons.

A third method is to use the Python built-in module functools. Functools offsers the partial object, which "freezes" a command and a set of arguments into a callable package.

from functools import partial
def FuncBoxes():

# note the comma - the command is an argument to partial!
small_box = partial( cmds.polyCube, d =2, w = 2 , h = 2 )
med_box = partial( cmds.polyCube, d = 5, w = 5 , h = 5 )
big_box = partial( cmds.polyCube, d = 10, w = 10 , h = 10 )

my_w = cmds.window()
cmds.button("small box", c = lambda _ : small_box())
cmds.button("medium box", c = lambda _ : med_box() )
cmds.button("large box", c = lambda _ : big_box() )

Partials are handy for cleaning up the messes you'd get from trying to format a complex commands in-line in the middle of your gui code. This example is a sort of worst case scenario, since Maya buttons always fire with a single argument and cmds.polyCube doesn't like that.  Here I used lambdas   lambdas to swallow the arguments  - note the telltale underscores. More often you'll be calling your own functions and the syntax is much cleaner and easier to parse:

    from functools import partial
def FuncBoxesClean():
        def make_box(_, **kwargs):
           # swallow the argument but keep the keywords...
        small_box = partial( make_box,   d =2, w = 2 , h = 2 )
med_box = partial( make_box, d = 5, w = 5 , h = 5 )
big_box = partial( make_box, d = 10, w = 10 , h = 10 )

my_w = cmds.window()
cmds.button("small box", c = small_box)
cmds.button("medium box", c = med_box )
cmds.button("large box", c = big_box )

Final Summation

So, here's a cheatsheet of the rules for hooking maya event callbacks:

  1. Don't use strings for python calls. 
    1. If you're calling MEL, OK: but don't use MEL anyway :)
  2. Pass functions to your callback directly. No quotes, no parens.
    1. If you have a scope problem, you'll see it when you create the GUI; usually you can solve it with an import
  3. If you need to pass arguments to your function in the callback, you have options:
    1. custom mini-functions are clear, but extra work
    2. lambdas are ugly, but workable
    3. partials - especially on top of your own functions - are clean 

Now, even if you follow these rules,  its easy for your functional code and your GUI to get in each other's ways.  Creating a lot of throwaway functions is busywork, but formatting commands in-line inside GUI code is error prone and hard to read. Partials are nice for separating data from layout code, but usually come with annoying extra syntax to hide the callback arguments.

Next Episode...

 Of course if you've been following the mGui series you'll know where I'm going. (If you haven't, you might want to check here, here and here before continuing).  Next time out I'lll take a look at how you could get to a cleaner separation of concerns like this:

    import mGui.gui as mg
    def make_box(*args, **kwargs):
H,W,D = kwargs['sender'].Tag
cmds.polyCube(h = H, d = D, w = W)

def mGuiBoxes():
with mg.Window("boxes") as window:
with mg.ColumnLayout("col"):
mg.Button("sm", label = "small boxes", tag = (2,2,2) )
mg.Button("med", label = "medium boxes", tag = (5,5,5) )
mg.Button("lrg", label = "large boxes", tag = (10,10,10) )

for b in window.col.Controls:
b.command += make_box

by Steve Theodore ( at April 23, 2014 08:53 PM

Red9 Studio Pack v1.42 released

So it's been a while since the last release but now everything is on GitHub it should be a lot easier to manage releases, I'm just going to take tags so you get full access to everything. Also the main branch of code is the gitHub trunk so you see any changes as I commit them on a daily basis.

Download is a lot easier than on GoogleDrive, just click on the tag and there's a link to download as a zip.

cheers and keep the suggesttions and comments comming, they do help!


Highlights of the release are also commented up there but just in case:


  • New - Red9_Audio - added BWav support to the AudioNode. This allows you to inspect and retrieve Timecode data from any broadcast wav
  • New - Red9_Audio - added a new 'inspect' item to the Red9 sound menu in the Trax for inspecting the internal properties of the selected sound node
  • Mod - Red9_Anim - Stabilizer wasn't respecting the keyer groups when you set it to just track 'rots' or 'trans'
  • New - Red9_Anim - CopyKeys now deals with animLayer by pre-merging the data to a temp cache before copying the merged data. The original layers are left in tact!
  • New - Red9_Audio - added a function getMediaFileMetaData() which is capable of extracting metaData from most media file types via ffprobe.exe
  • Mod - Red9_Pose - added a filterMap param to the poseCompare call. This can be used as a master core list, only nodes that appear on this will be checked against. Useful for checking core skeleton nodes whilst ingnoring others
  • Mod - Red9_Pose - added an ignoreBlocks to the poseCompare call. This allows the code to ignore complete sections of the compare data, we mainly use this for ['missingKeys'] so that the compare skips them
  • Mod - Red9_Anim - modified the AnimUI behaviour such that the 'CTRL' key modifier is now stored with the UI. If you laucnh the UI with ctrl held it'll toggle between launching as docked or not.
  • Mod - Red9_Anim - exposed the snapTranslates and snapRotatesflags to the snap() call in AnimFunctions Mod - Red9_Core - Mods to the LockChannels class for better behaviour, set hierarchy flag to false by default.
  • New - Red9_Meta - new flag exposed at class level '_forceAsMeta'. This forces the code to ALWAYS return instansiated MetaClass objects when dealing with nodes rather than dag paths.

by Mark Jackson ( at April 23, 2014 08:38 PM

Zbrush Speed Sculpt: Space Cop

I actually took a little over an hour on this one. Theme this week was space cop, so I made a space coppish thing.

"Space license please..."

by Peter Hanshaw ( at April 23, 2014 05:22 PM

April 22, 2014

Rigging Dojo’s Artist in Residence (AIR) : April – David Bokser

Wednesday April 23rd 11/10 central time (8 on the west coast)   To subscribe – After you complete payment click “return to Rigging Dojo” and then “register” with FirstnameLastname format. …

The post Rigging Dojo’s Artist in Residence (AIR) : April – David Bokser appeared first on Rigging Dojo.

by Rigging Dojo at April 22, 2014 04:04 PM

April 21, 2014

Fixed rotator

What's on today:
  • Fixed rotator.
  • Fixed rotator with steps.
  • Quick look at vector graphics textures in after effect.

Fixed rotator.

How to rotate a texture by a certain fixed angle?

Just input a constant value in the time pin of your rotator. The constant value is like saying ‘this is where the rotator would get you in that much time’. (I mean, not technically since the unit is not seconds but it's sort of a way of seeing it.)

What to expect from that value? First thing, set your rotator speed to 1. Then you have to know that unreal uses radians as a rotation unit.
180 degrees = π (pi, the maths symbol equal to 3.14159 and so on).

Therefore, 90 degrees is going to be π / 2.
Good thing to know: you can do maths in input fields. In your constant input, you can simply type in 3.14159/2.


You could use a fixed rotator to represent a circular gauge for instance. Most likely, the gameplay code is going to provide you with some normalized parameter. (Frankly that’s the best option. Sure modifying the value to fill your needs will add a few instructions but it gives you the flexibility to easily do anything you want with it, rather than having to go bug a coder so he changes the value he's hard coded for you.)

Modify the input to the range you are interested in (say 0 to 90 degrees), plug it into the rotator, and then into a mask that multiplies your gauge.

You’ll notice that I’ve just multiplied my normalised value; since my min is 0, I don’t need to use a lerp which would add more instructions for the same result.

Fixed rotator with steps.

We can go one step further.
In Enslaved, Trip has an emp gauge which recharges over time and is split in several chunks which appear one at a time.

I used a similar setting, with a rotating mask that multiplied my texture, only this time the rotation value had to be contained until it reached the next step.

In the following example our gauge value is at 0.5, that's half filled. The gauge is split into 5 chunks, so each step is 0.2.

We check: our many steps are there in our current value ? That's 0.5 / 0.2 = 2.5. We're only interested in full steps so we floor it.
We've then got two full steps, the step size is 0.2 that's 0.2*2, our output value is 0.4.
The floor node is going to keep the value contained at 0.4 until the next integer is reached. When we get to 3 full steps, the output will suddenly jump to 0.6 and so on.

The input value is divided by your step size, floored and then multiplied by your step size.
Credit for this little setup goes to Gavin Costello, currently Lead Programmer at Ninja Theory and full time graphics programming genius.

Vector graphics textures in after effect.

 I've got a thing for after effects in general, and I find it excellent for vector graphics in particular. It's very flexible, the textures are easily scaled and iterated. Illustrator could do the same but with after effects you can also animate everything for previs and/or flipbook generation. (Which is exactly how I worked during Enslaved. My previs and textures were the same assets.)

Here's the way I made the previous gauge for instance, using shape layers:

2 ellipses, 6 rectangles, a subtract merge to cut out the inner circle and the rectangles, another rectangle and an intersect merge to extract the bottom left quarter. And finally a white fill of course.

I like to use expressions even with these sort of very simple shapes. It is a small time saver as you make the texture and might be a massive one along the project as you iterate on your texture.

Right there for instance, I link the anchor point to my rectangle size for the anchor to be at the end of the rectangle rather than in the default centre. Sure I could have done it by hand but I find it better when automated. I was a bit lasy in this case so I stopped there but if I had created this texture for production, I would have also:
  • linked the size of every rectangle to the first rectangle (or even neater, to a slider control)
  • linked the rotation of every rectangle to a slider control (and multiplied it by 2, 3, 4 etc. for each next rectangle)
  • and maybe controlled the radius of each ellipse from a slider parameter too, just so as to modify everything for one single place and not have to open up the content properties

by mkalt0235 ( at April 21, 2014 01:39 PM

goless- Golang semantics in Python

The goless library provides Go programming language semantics built on top of Stackless Python or gevent.* Here is a Go example using channels and Select converted to goless:

c1 = goless.chan()
c2 = goless.chan()

def func1():

def func2():

for i in range(2):
    case, val =[goless.rcase(c1), goless.rcase(c2)])

While I am not usually a Go programmer, I am a big fan of its style and patterns. goless provides the familiarity and practicality of Python while better enabling the asynchronous/concurrent programming style of Go. Right now it includes:

  • Synchronous/unbuffered channels (send and recv block waiting for a receiver or sender).
  • Buffered channels (send blocks if buffer is full, recv blocks if buffer is empty).
  • Asynchronous channels (do not exist in Go. Send never blocks, recv blocks if buffer is empty).
  • The select function (like reflect.Select, since Python does not have anonymous blocks we could not replicate Go’s Select statement).
  • The go function (runs a function in a tasklet/greenlet).

goless is pretty well documented and tested, but please take a look or give it a try and tell us what you think here or on GitHub’s issues. I’m especially interested in adding more Go examples converted to use goless, or other Go features replicated to create better asynchronous programs.**

*. goless was written at the PyCon 2014 sprints by myself, Carlos Knippschild, and Simon Konig, with help from Kristjan Valur Jonsson and Andrew Francis (sorry the lack of accents here, I am on an unfamiliar computer). Carlos and myself were both laid off while at PyCon- if you have an interesting job opportunity for either of us, please send me an email:

**. We are close to getting PyPy support working through its implementation. There are some lingering issues in the tests (though the examples and other ‘happy path’ code works fine under PyPy). I’ll post again and bump the version when it’s working.

by Rob Galanakis at April 21, 2014 01:34 PM

Planet of the Apes World First Look


Empire Magazine has a first reaction to some footage that was recently screened from DotPotA, as well as a few images from the film. I’m pretty excited about how this movie is shaping up.

by Morgan Loomis at April 21, 2014 02:38 AM

April 20, 2014

Photoshop tip: sample all layers.

Today I came across this website which I highly recommend:

It can look a bit dry at first sight but don't let it put you off. It's very clear, thorough and well described with examples.

The tip I found today works for the blur and the healing patch tool (perhaps more tools have that option).

If you tick the Sample All Layers option, you can paint your blur or healing on an empty layer which will act as an adjustment layer. The layers underneath will appear blur but will remain unaffected.
Neat innit?

by mkalt0235 ( at April 20, 2014 04:26 PM

April 19, 2014

Classic CG: La plus ca change

From SIGGRAPH 1992.

While I'd hesitate to call this a 'classic' it does prove one thing: tacky me-too crap is eternal  Hands up if you remember palette animations!  (For that matter, walkers and canes up if you remember the local-cable ads this thing was parodying!)

by Steve Theodore ( at April 19, 2014 03:50 PM

3dPrint: Goblin!

I finally got this guy printed from Shapeways. It's the largest model I have printed so far, standing at 12cm. I was able to get the price down using a bunch of tools in MeshLab, Zbrush and Maya to ensure it had a physically accurate minimum wall thickness of 0.725mm throughout the entire model, reducing the material density to a crazy 1.87%.

The details held up pretty well. The only areas that didn't were the toe nails, finger nails and the chain-mail overall, which was expected.

The print stands at 120mm tall, and is printed in the Fine Polyamide (Nylon) material.

The render of the model from ZBrush. 

by Peter Hanshaw ( at April 19, 2014 03:45 PM

April 18, 2014

Roger Roger

If you've been playing with the stansdaloneRPC server, I've added a new branch to the github project that includes a minimal level of password security. It's still not the kind of security you want if this is to be exposed to the wicked world, but it should suffice to keep you free from teammates who want to prank you.

Comments / bug reports and pull request welcome!  If you use the github wiki to log an issue or ask a question, it's a good idea to put a comment here to make sure I see it.

by Steve Theodore ( at April 18, 2014 05:17 AM

April 17, 2014

Torus Knots

Made a torus knot from a line and an attrib vop. I was able to made a few different types after I figured out the basics from wikipedia, and a few other sites around the web…

by Ian at April 17, 2014 11:59 PM

April 16, 2014

A video is worth 30,000 words per second?

    Just popping in to say that if you haven't checked out my Vimeo channel recently...err...there's not much new stuff there, but there's more fun stuff coming (along with new blog posts that I've promised people and myself).  I'm considering doing some Cinder video tutorials if I ever find some free time, not sure about what, maybe covering Kinect, the Intel depth cameras, that sort of thing...not sure really, we'll see.  But anyway, yeah, bookmark my channel, show your friends, loved ones, your inner circle, all that.  If nothing else, it's good for a tiny bit of inspiration...maybe.

>>> Me on Vimeo <<<

by Seth Gibson ( at April 16, 2014 06:01 AM

Results are not the point?

The phrase “results are not the point” often confuses people new to Lean thinking. It confused the shit out of me, not having really understood it even after my first few books. This is a shame, because it’s such a simple thing.

On Friday night, Danny got really drunk, coded a game, and the game was a hit. Danny did this again the following Friday, with the same results. And once more that third Friday.
Jess codes on sober Saturday nights instead (still drinks on Friday). Jess programs a game, and it runs poorly, crashes often, and isn’t fun. The following Saturday, Jess makes a new game, which runs fast but still isn’t fun and crashes often. That third Saturday, Jess creates a new well-performing, fun game, though it still crashes.
Would you bet on the long-term success of Danny or Jess?

Clearly, the better bet here is Jess. Jess has discovered a process which can be continuously improved. There is good reason to believe Jess will eventually create reliable success. The fact that Danny has been successful three times is basically irrelevant, since Danny’s process is totally haphazard.

This is the idea behind results are not the point. Focusing on the results, and not how those results were achieved, doesn’t improve anything in the long term. The point is to create a repeatable, empirical, continuously improving process. If we can create a reliable, successful process (which here includes culture and practices), we can get reliable, successful results.

by Rob Galanakis at April 16, 2014 02:43 AM

April 15, 2014

Autodesk 3ds Max 2015 released

And with it, the Python API!

Which comes with something really interesting now… PySide 2.1 is built-in! Really cool :)

Anyway, bookmark this URL, you will probably need it during the next year:

by Artur Leao at April 15, 2014 08:42 PM

Rigging Dojo’s Artist in Residence (AIR) : April GDC Wrap up

To subscribe – After you complete payment click “return to Rigging Dojo” and then “register” with  FirstnameLastname format. Hi All, Chad here. Well GDC and several production milestones bumped out …

The post Rigging Dojo’s Artist in Residence (AIR) : April GDC Wrap up appeared first on Rigging Dojo.

by Rigging Dojo at April 15, 2014 02:32 PM

Coding a Maya Production Pipeline with MetaData

Heads up.. I'm going to be doing a presentation at Develop in Brighton this year about how to utilize Red9Meta in a production pipeline, running through some internal examples of the tools and Maya dag structures that we're currently working on at Crytek. 

This will be an overview really, how and why metaData helps not just in constructing complex setups, everything from Exporter, Facial and Rigging pipelines, but also as a light coding api to deal more seamlessly with nodes in Maya.

For all of those doing the Rigging Dojo Character Engineering course, might be a good chance to catchup.

More details to follow but if there's anything in particular that you'd like me to include drop me a mail. 



by Mark Jackson ( at April 15, 2014 11:21 AM

Grab 3dsmax viewport with python – Part 2

I finally managed to get the viewport grabbing to work with python only, no hacks using MaxPlus.Core.EvalMAXscript(). More or less :) It’s working for the standard viewport.getViewportDib() equivalent in Maxscript, I still can’t figure out how to grab it directly from the GraphicsWindow (gw.getViewportDib()) to get a clean viewport snapshot without any overlays like gizmos, etc. This method works only in 2015.

Long story short, I’ve updated the YCDIVFX MaxPlus Packages in github and even added a new package called maxhelpers where I will put code that will be reused across all other packages.


Here’s how the code looks:

def ActiveViewport(self, filename=(MaxPlus.PathManager.GetRenderOutputDir()
                                   + r'\default.jpg')):
        """Grabs viewport to a file on the hard-drive using default viewport size.

        :param str filename: a valid path to an image file

        :rtype:  MaxPlus.Bitmap
        # Create storage
        storage = MaxPlus.Factory.CreateStorage(BitmapTypes.BMM_TRUE_64)

        # Create BitmapInfo
        bmi = storage.GetBitmapInfo()

        # Set filename

        # Create bitmap to hold the dib
        bmp = MaxPlus.Factory.CreateBitmap()

        # Viewport Manager
        vm = MaxPlus.ViewportManager
        # Get active viewport
        av = vm.GetActiveViewport()
        # Grab the viewport dib into the bitmap
        av.GetDIB(bmi, bmp)

        # Open bitmap for writing

        return bmp

To grab and display the bitmap in max you just need to do this:

bitmap = grabActiveViewport()

Hope this is useful!

by Artur Leao at April 15, 2014 11:10 AM

April 14, 2014

Sweet Sumotori Dreams

I had no idea that the genius behind Sumotori Dreams is still making awesome procedural animation toys.

If you're not familiar with Sumotori Dreams, it's the funniest thing that ever happened to procedual animation.  Proof here (loud cackling and some profanity in the audio track, could not find any that did not have lots of hilarity and shouting):

If you're at all interested in procedural animation - or have even a tiny sliver of a sense of humor - you should buy the iPhone app the android app, or the PC version.  This guys deserves our support!

On a related note, if you like this you may find this talk from the developer of Overgrowth interesting as well.

by Steve Theodore ( at April 14, 2014 09:23 PM

Super simple manual flipbook.

Say you want to manually animate a flipbook in matinee.
Here's a very simplified setup for it. The downside is: is has to be one single row but you can modify the amount of columns as you wish.

There could be less instructions but the idea is to make it easy to instance. In the material instance, you just have to modify the FramesAmount parameter and you can animate the FrameIndex parameter in matinee with everything working fine.

What's happening there?
You simple tile and shift your texture.


In this case, one tile is a forth of the texture size. So you divide 1 by the amount of horizontal frames in your texture and multiply only the U of your texture coordinates.
You've got the correct tile size.


To display the correct frame at the right moment, you'll just need to move the texture horizontally.
The amount by which you need to shift the texture to reach the next frame is one nth of your texture, n being th amount of horizontal frames. (4 in this case)
The FrameIndex value (animated from matinee) multiplies this to find how many times you need to shift it.

The floor node is there to ensure you only display full frames.
Since the frame index is floored, your index will be starting at 0, remember this when you control the value in matinee. To animate a four-frames texture, the value will have to interpolate between 0 and 3. (If your texture is set to wrap, a FrameIndex of four will get you back to displaying frame 0.)

Not super clean or flexible but definitely super simple.

by mkalt0235 ( at April 14, 2014 05:39 PM

Get 3dsMax viewport HWND

In Maxscript! I’ll just leave it here as a reminder and hopefully it will be useful for you too!~

 assembly = dotnet.loadAssembly "Autodesk.Max"
 g = (dotnetClass "Autodesk.Max.GlobalInterface").Instance
 inface = g.CoreInterface
 activeview = inface.ActiveViewExp
 print activeview.Hwnd

by Artur Leao at April 14, 2014 11:37 AM

April 13, 2014

Warning: Garish graphics ahead!

If you're tired of boring old light-grey-on-dark-grey text, you'l'l be pleased to know that the Maya text widget actually supports a surprising amount of HTML markup. Which means that instead of this:

You set peoples eyeballs on fire like this:

This is a single cmds.text object  with it's  label property set to an HTML string.  

It turns out that cmds.text is actually a fairly full-featured HTML4 renderer! That means that you can create pretty complex layouts using many -- though not all -- of the same tools you'd use for laying out a web page.  You can style your text with different fonts, sizes, colors, alignments and so on - you can even us CSS style sheets for consistency and flexibility.

More than that you can also include images, tables and layout divisions, which are great for formatting complex information.  No more printing out reports into dull old textScrollFields!

Best of all, it's trivial to do.

All you need to do is set the label property of a cmds.text object to a striing of valid HTML. By default your object inherits the standard maya background and foreground colors but you can override these in your HTML  You can even just compose your text in an HTML editor like DreamWeaver or Expression Blend; that how I did the example in the graphic above..

There are some limitations you need to be aware of.  The big ones seem to be:

  • HTML/CSS controls for positioning text or divs don't seem to work. Align tags inside a span element do work, but float and positions apparently do not.
  • The renderer won't fetch images or other resources from a URL or relative paths.
  • No JavaScripts - so no blinking texts or animated gifs.  I'm not sure that's a loss.
  • No inputs such as buttons, checkboxes or text fields.
  • Fonts seem to render smaller inside the Maya text than they do in a conventional browser. You can't specify text size in ems or percentages; pixel sizes seem to work fine, however.
  • It looks like text is the only control that supports this styling right now ( tested in Maya 2014).
I'd assume that these limitation reflect the behavior of underlying QWidgets inside of Maya - if anybody has the real dope to supplement my guesswork, please chime in.   

In the mean time, here's to the inevitable avalanche of eye-ripping garishness that is sure to result from this revelation. As for me, I'm off to go convert my whole toolset to Comic Sans! 

by Steve Theodore ( at April 13, 2014 12:59 AM

April 12, 2014

The Last of Us: Remastered (PS4) !

My last project, The Last of Us us getting Remastered release on PS4 with a special dose of HD All-The-Things !

Check out the info here: Playstation Blog

by Nathan at April 12, 2014 09:21 PM

The “Year of Code” Director is Your Boss

There was some hubbub a few months ago when it was revealed the Executive Director of the UK’s Year of Code initiative can’t code [link]. Not that technical (domain) competency is a sufficient condition for management and leadership, but I’d certainly call it a necessary condition. (I’ll use the world ‘technical’ below to refer to any sort of specialized domain, not just programming.)

Apparently a number of people don’t agree with the idea that competency in a domain is a requirement to manage that domain.* I find this idea infuriating and it can only end poorly.

Perhaps you have a manager who knows little about programming or design or whatever your specialty is, and you consider this person to be the best boss of all time. Great! I’ll call this person Your Boss for convenience. Here’s the problem:

At some point, Your Boss needs to make some contentious decisions. Maybe over your work, maybe over something you’re not directly involved with (I bet Your Boss was hated by a lot of people, too!). Your Boss has literally no ability to help resolve a technical decision. “Wait!” I hear you say. “My Boss is enlightened enough to know that the people closer to the problem should be making the decision!

But who are those people closer to the problem? Who put them there? Oh, that’s right: Your Boss. But your boss has little technical knowledge. How is Your Boss supposed to decide who makes the more technical decisions? Without basic technical ability, Your Boss doesn’t even know what questions to ask. Your Boss can’t even learn; she doesn’t have the technical prerequisites. Instead of being able to provide leadership, Your Boss is left scratching her head. This is not leadership, and this is not management. This is a cancer and an organization that is unable to grow and learn.

It’s no surprise this topic is divisive. When Your Boss places a lot of trust in you, you are autonomous and think of Your Boss as the best boss of all time. But when someone runs up against you and Your Boss, they have no real recourse, because Your Boss trusts you and has no ability to further inspect the situation.

Certainly, superior ability or experience is not a requirement for management over a domain. But I thoroughly believe that not just familiarity, but some actual professional practice, with a domain is a requirement. I hope that if you are someone who believes in the myth of the competent non-technical manager, you’ll rethink your experience and view Your Boss in a more complete light.

* Clearly, at some point, you cannot be experienced in all the domains you manage, and need to trust people. Unfortunately we do this far to soon, and accept a development manager who has not developed, or an engineering manager who has not done much programming. In the case of the Year of Code Director, I think the issue is a non-technical background (in programming nor teaching) and a general lack of experience. If she had proven a wunderkind in her given field (which is, btw, basically PR/marketing/communications), maybe she should be given the benefit of the doubt. There are many examples where talented administrators have moved into new areas and been quite successful. But her appointment, along with most of the rest of the board, is pretty clear cronyism (and when you throw out technical merit and domain experience, you’re left pretty much with cronyism).

by Rob Galanakis at April 12, 2014 07:59 PM

Adobe Photoshop CC- Not exactly an early adopter...

Its a words post! Where is the code? That comes later... for now, words. I'm now an official owner (or at least subscriber) of/to Adobe CC... yeah, I got through my whining stage and now I'm learning to love the cloud, or, at least learning how to accept the inevitable.
Long live the cloud! I guess.

But from the Photoshop tool development perspective I think I actually find it a little more exciting than I am letting on. You mean *everyone* will be on a standard version? No kidding! What an awesome development! No more hacking in (sometimes) seeming random version numbers! The ability to assume everything you want to support is supported. Great! Don't have the right version? Update your Photoshop buddy!

When taken from that perspective I really think that Adobe's decision (aside from the whole aspect of never actually 'owning' the software) is a pretty great one. Maintaining pipelines for multiple versions of Photoshop ceases to be a major problem*, and tool development and distribution becomes, if not simpler, at least a little more direct in execution. 

I'm also taking my first steps into the Photoshop SDK, which is an incredibly powerful and daunting piece of architecture. Not only does it require C++ for creating plug-ins, but it also seems to be half way between a development framework and a history lesson on ye early days of Photoshop. And the documentation? Reading through it, there seems to be a big Photoshop SDK tutorial shaped hole where the SDK tutorials ought to be. 

But, if it were easy, it wouldn't be as fun! Now to work through that hello world tutorial...

* Currently supporting four different versions at work and trying very hard not to.  

by Peter Hanshaw ( at April 12, 2014 03:36 PM