We've Moved


The blog has been retired - it's up for legacy reasons, but these days I'm blogging at blog.theodox.com. All of the content from this site has been replicated there, and that's where all of the new content will be posted. The new feed is here

Wednesday, January 29, 2014

Talking to Photoshop via TCP

A recent thread on TAO got me thinking about communicating with Photoshop from Python. In the past I've done this Adam Plechter-style using COM ( from both C# and Python) and it's worked for me, but I know some people have problems with COM for a variety of reasons relating to windows versions and DLL hell.
Using COM always makes me feel like Mr. Yuk.


Since I had to learn a few JavaScript tricks back when I started looking into Python web development, I thought it might be worth investigating what could be done to cut out COM.  It turns out that ExtendScript - the JavaScript flavor that comes with Adobe products (at least since CS 5, and I think earlier) includes a socket object which allows for TCP communication.  That's not kosher in 'real' browser based JS - but ExtendScript cheats a little (that's also why it allows you to do things like hit the local file system, another JS no-no).

The Adobe docs have an example (around page 196) which shows how you can implement a chat server or a web server running inside Photoshop. That might not be practical but it provides a simple framework you can hijack to turn Photoshop into a remote procedure call server.  Here's an example of a super-simple server that can be run inside Photoshop:
Note: You'll need to save the file as a .JSX (not .JS!) for Photoshop to allow the ExtendScript functionality which makes the socket objects work.

This example is very bare bones, but it is easy to extend - just create function objects and add them to known_commands dictionary and then send them over the socket. The way it's written here the commands and arguments are split on spaces (similar to the way a shell command works) -- if you need to get at them in your JS functions you can get at them using the arguments() keyword:.  For serious work I'd probably use the ExtendScript XML object and send the commands and responses as xml since that gets you out of having to worry about stuff like 'what if I want to send an argument with a space in it' -- however this purpose of this excersize is just to demonstrate what's possible.

I should note that while the server is running, Photoshop is locked into the wait loop so it will not be accessible interactively -- like a Maya running a long script, the main thread is just waiting for the script to continue. For the typical 'remote control' application that's what you'd expect, but it may not answer for all purposes - so be warned.

If you're trying to talk to Photoshop from Python, it's incredibly simple:


That's all there is to it - of course, the real problem is getting useful work done in the clunky Photoshop API -- but that's going to be the same no matter whether you talk to PS via COM or TCP/IP.  Anecdotally, I've heard the PS scripts are faster in JavaScript than when using COM or AppleTalk or VB, so perhaps this method will be competitive on speed as well.  For small tasks it's certainly a simpler and less irritating way to send a squirt to and from PS 

Friday, January 17, 2014

No Humans Involved (Classic CG)

Saw a great paper in +Robert Butterworth 's feed which reminded me of this classic SIGGRAPH presentation from 1994 - one of the all time greats in procedural animation.  None of the motion in this was animated by hand :)

Thursday, January 16, 2014

Mighty Morphin Maya Module Manager

For folks who are interested in fiddling with Maya modules as per the last post, I've tossed a quickie class to manage Maya modules onto my Gist account.

This is a bare bones bit of code. The main class is the ModuleManager which can find any .mod files on the MAYA_MODULE_PATH of the current Maya environment. It's primary use is to find and list all the modules; secondarily it can be used to toggle them on and off (by changing the leading + which Maya uses to id a module to a -, or vice-versa). It's pretty dumb (no accounting for file permissions, incorrectly formatted .mod files, etc) but it's handy for quickly testing out configs.

 Also included is a GUI class, ModuleManagerDialog, which finds provides a simple GUI for listing, enabling, and disabling .mod files. Again, pretty simple stuff, but people may find it useful.


As will all code I put up here, it's BSD licensed. Use away with attribution - and if you have any bug fixes, let me know and we'll fix the Gist version.

PS, hat tip to the gang at TAO  for the idea of using a context manager to get out of all those stupid setParent("..") calls in Maya GUI work. I could not find the original post where somebody mentioned it - but whoever you are, sir or madam, thank you ever so much.


Sunday, January 12, 2014

Maya's (mildy) Magical Modules

If you're doing tools work in Maya, you've probably seen a lot of ways of distributing scripts and tools. The most common, alas, is also the wonkiest - dumping a bunch of scripts into the users's script folder or maybe fiddling with Maya.env to point at a shared drive on the local network.  

Eventually, you'll rum into situations where you can't just litter files all over your user's machine. Maybe you're supporting multiple projects in the studio and you need to have users hopping back and forth between toolsets. Maybe you're dealing with multiple versions of maya and your tools have been forked into different environments. Maybe you're dealing with outsources who don't want to permanently alter their own environment just to get your tools for one project. Whatever the reason, someday you'll run into a situation where you want to be able to drop in a whole maya toolset as a unit and to remove or disable it the same way.

Maya has always a had a facility called 'modules', can be useful for this purpose. It used to be used for things like  cloth sim or hair that were tacked on to the main package (back when hair was a $9,000 add-on!) .  Since then it's largely fallen out of favor, but it's never gone away -- and it has some useful properties that are great for tools distribution.

A module is really a sort of virtual file system.  Maya's most important folders are those for scripts, plugins, icons, and presets -- the same ones you typically see in your maya user directory.  If you place a special text file in your module path, you can add to extra paths for maya to search for scripts, plugins, icons and presets.

The big advantage of modules is the ability to manage complete set of files at once - scripts, plugins and so on can all be included (or excluded) from your Maya in a single place (Hint to Autodesk - it would be very nice to give users a UI for this.  The 'Modules' setting in the preferences, confusingly, does not refer to these modules.).  Parts of a module can be kept independent of the user's private stock of scripts and tools. For outsourcers and contractors, in particular, the ability to completely uninstall a toolset you don't need anymore is a godsend.

Another handy feature of modules is that they can point Maya at shared network paths as well as paths on disk. This makes it far easier to keep a whole team on a common tool set by pointing a module script directory at a shared drive.  I'm not personally a big fan of the shared drive as a distribution method (more on that some other time) but it's fairly common and this makes it easy to establish and manage.

Finally modules do a little bit of automatic versioning. A module can specify different paths for different Maya versions, OS'es and processors.  This means a tool author can produce a single distribution for all customers and give them identical installation instructions without worrying too much about the details of the individual workstations on the receiving end.  You can pack up all of your distributions into a single zip file, and users merely unzip it into the modules folder of their local settings and the installation is done .

Basic Setup

Modules are defined by a simple text file with a ".mod" extension.  Maya will look for module files in all the directories in the MAYA_MODULE_PATH environment variable if you have it set, or in the modules directory of your maya user directory (for example, in My Documents\maya\modules). Here are the default locations where modules can be placed:

Default for Windows

/My Documents/maya//modules
/My Documents/maya/modules
C:/Program Files/Common Files/Autodesk Shared/Modules/maya/
C:/Program Files/Common Files/Autodesk Shared/Modules/maya/modules/

Default for Mac OS X, Linux

$MAYA_APP_DIR/maya//modules
$MAYA_APP_DIR/maya/modules
/usr/autodesk/modules/maya/
/usr

A module file is a plain text file. The official documentation for the format is here (the 2012 version is here - there are some pretty significant differences between 2012 and 2013+), but for the simple case it looks like this

+ moduleName 1.0 c:\path\to\moduleName

The module name is what shows up in the Maya UI, the 1.0 is a version number, and the path points Maya to the location of the module folder.  This example would point to a folder called "ModuleName". If that's all that goes in there, then maya will look for scripts in

c:\path\to\moduleName\scripts

icons in

c:\path\to\moduleName\icons

and plugins in

c:\path\to\moduleName\plug-ins

You can test this out by making the folder c:\path\to\moduleName\scripts and popping a userSetup.py ( or a userSetup.mel, if you're feeling old school) into it with a print("hello world")  in it. When you restart your maya you should see it printout in the maya output window as Maya loads. It's the output window for python because the script will execute before the Maya UI has loaded (something to keep in mind in your startup scripts!).

You can avoid hard coding the paths by using an alternate syntax in the mode file.  You have a can, for example, include an environment variable using ${variable} in place of an absolute path. For example


+ moduleName 1.0 ${PROJECT_LOC}\mayatools


will point look for your module folder in the location defined by the environment variable named project_loc.  One side effect of this is that uou can put your modules into the modules folder (alongside your mod files) without having to hard-code paths:

+ moduleName 1.0 ${MAYA_MODULE_PATH}\moduleName 

 You can even point your modules at a network share:

+ moduleName 1.0 \\net\shared\modules\moduleName

 

Fancy stuff in 2013+ 


Sometimes setup requirements are more complex than dropping in a single folder (not a good idea if you can avoid it - but you know how it is...). In 2013 and later,  you can use the module file to override particular paths and do some more flexible setup.

For example, in 2013+, you can also define the path relative to the module file location:

+ moduleName 1.0 ..\moduleName

should point at a folder above the directory where the module file is located. Just remmber: 2013+ only...  If you've looked at, for example, Cyrille Fauvel's post on modules you'll see a bunch of advice that only works for 2013+.  Be aware :)

The other neat addition in 2013 is the ability to override specific sub-paths of the module. Thus

+ moduleName 1.0 ${MAYA_MODULE_PATH}\moduleName 
scripts: //network/share/scripts

would point the scripts at the shared network scripts  drive, while leaving the plugins and icons folders in the module location.

 2013+ also offers the ability to set environment variables from inside a module

+ moduleName 1.0 ${MAYA_MODULE_PATH}\moduleName 
HAS_FANCY_2013_FEATURES=YES

Will set the environment variable HAS_FANCY_2013_FEATURES to 'yes' inside your maya session. 2013 even has some funky syntax allowing you to append to existing variables and so on . There's no point in recapitulating it here, the description in the docs is a concise as I could do - my only commentary would be: seriously, people? It's 2014 and still with the funky syntax crap?  I'm supposed to recognize that  PATH += and PATH +:= bin are different? 

Adding to paths can be extremely handy for some things, but unfortunately this won't solve a particularly thorny problem for tools teams, which is making sure that the OS can find binary dlls: if you have dll dependencies in a binary plugin, its 'too late' to fix it by appending the dll locatins to the system path once Maya has loaded. Which stinks -- and is another reason to avoid binary plugins like the plague they are if at all possible.


 One stop tool distribution... or not

For any busy TA, the business of distributing and maintaining toolsets on lots of other people's computers is a constant headache.  Modules can offer some significant assistance with this, since they allow you some control over Maya's search paths without requiring your to write code. It's a simple solution to the chicken-and-egg problem of getting Maya to do what you want before you've got tools loaded up to tell Maya what to do.

Modules, on their own, are not a complete solution to that problem.  But they are a help.

Modules basically just tell maya to look in a few extra places for things like scripts or plugins - they don't automatically run startup code or do initialization, and they don't have much in the way of smarts -- they can't for example, allow you to quickly switch between two project-specific versions of your maya tool set. You can install modules for toolsetA and toolsetB, but something smarter than a module file has to decide which one to pick on a given occasion (I'm going to talk about that problem in another post, but for now  However they do allow you to mix and match install locations, which is a big start; they also allow you to install outside the user's script folders, which is a key element in the good-fences-make-good-neighbors relationship between TA tools and the user's private maya preferences.  Plus, they are explicit: you can look at the file and tell what its trying to do, which beats any number of magic naming and location rules.

At some point in the near future I'll try to post something on the second half of the distribution puzzle: running startup code cleanly and intelligently.


Friday, January 10, 2014

Schadenfreude alert!

What does it say when a book titled Javascript:The Good Parts has a final chapter, "Beautiful Features", that's 3 pages long.

And is followed by a 7 page appendix titled "Awful Parts".

And another 7 page appendix titled "Bad Parts".


Unity crash course coming up...

Just a reminder - if you know anybody who want's to learn Unity in a hurry, there's still time to sign up for  Introduction to Game Development With Unity @ Makerhaus

Tuesday, January 7, 2014

State of Decay moment of zen (and lighting)

Pastor Will found the Super Fudge Chunk.  
The whole scene (except for that one spot of sunlight and the window) is lit entirely by ambient lighting with some vertex lighting and (small) point lights in the windows. What a nightmare - whatever we do in the next game, it won't be this!

There's no lightmapping, thanks to a 24 hour continuous time of day cycle.  The houses could also be placed at any orientation, so the vertex lighting has to be pretty gentle - we shot basically ran a very smoothed out hemsipherical dome light outside the house, once with Final Gather and once with a dome of stochastically placed Maya point lights to create the illusion of darker interiors and lighter areas near windows). The final gather provided some bounce light look and the point lights provided a smooth gradient; blending the two helped eliminate hard artifacts and cut down on the frequency since we didn't want real shadows which would be wrong as the sun moved.   We also ran a similar omnidirectional vert light pass on all the props to add depth to things like cabinets and shelving. I also did a little tool to try to do some vertex decimation on the lit meshes -- we tended to subdivide the big planar areas heavily before lighting and then trim down verts which didn't hold much light or shading information (again, the smoother interior gradients helped a lot on memory here).

A handy trick - thanks to a suggestion from +Wolfgang Engel  - was small point lights in the windows and doors to add a boost to exterior areas and to make it look as if the characters move from light to shadow, which isn't really true.  These had to be very small - much smaller than I wanted - to keep the overdraw costs low in the Crytek deferred renderer. Every house had a handful of somewhat larger lights right near the floor. They watched the sun direction and lit up when the room was facing the sun to provide the illusion of bounce lighting (this wasn't really checking the sun, it was checking the orientation of the light (and the house) against a precalculated table of sun positions and elevations so you could figure out how bright a given vector should be at a given time. Would have been much nicer to really sample the sun , but the perf environment was brutal

The ambient lighting comes from an HDR cube map -  only one per building, alas, and they didn't orient with the placement.  In the end I hand painted abstract ones.  The 'faces' of the cube are color biased to help pick out the planes of the architecture so it doesn't all collapse into mono-color mush.

Thank God for screen space ambient occlusion, which adds a lot of edge definition.

And for Super Fudge Chunk.

PS: Who has seen the bug where Pastor Will flips out on somebody back at base and threatens to "Cut you like a prison bitch"?  We heard about it from a tester and unanimously voted it "Won't Fix".


Sunday, January 5, 2014

Future Watch: Graphics & Animation Research

I've added a permanent page to keep track of interesting academic research. It's over in the sidebar.  If you're at all into animation, download the videos or the ppt of the paper on gait optimization. It's fun -- unless you're an animator, in which case it's a bit scary.



The list is a bit skimpy atm, I'll post updates as it expands.


Friday, January 3, 2014

Unity class coming up at MakerHaus

I'll be teaching a Unity class at MakerHaus in Fremont starting January 13th.  This is an intro class but should be useful for anybody who has heard about Unity but doesn't use it professionally and would like to jumpstart the learning process.


First find of 2014: A Scriptable DCC app in the browser?!?!

Just stumbled across this link to clara.io, which is an in-browser 3D editor from Ecocortex, one of the vendors behind the Alembic file format. It's a 3d modeler that runs in a browser.  And it's free.

Sheesh.

And look at the bottom - a script console!

I'm not entirely sure what to make of this, beyond totally wicked. I'm not going to try to review clara here - I just found it today, after all, but it's such an interesting find that I can't help but comment.

The UI is a riff on familiar ideas, so it's easy to pick up for any TA, or for that matter anybody with more than a passing familiarity with the usual suspects. It doesn't break any new ground in productivity or ease of use - but that doesn't bother me in this context. Like the dog that walks on it's hind legs, it's not done perfectly but one is surprised to see it done at all.  This is, after all running in a browser. Yikes.

The app is still kind of wet behind the ears - it feels a trifle slow (some kinds of UI operations seem to trigger a perceptible refresh in all of the 3d views, for example). There are host of poly modeling tools -- notably booleans -- that Max/Maya users will miss pretty quickly.  All that said - the idea of running a zero-configuration, instantly deployable app from anywhere -- a tool you could be sure was the same for your outsources in Bangkok and on your own desktop right now -- along with inherent cloud data management?  The ability to run remote commands via web push commands, so you could set up a That is beyond just a curiosity. This app doesn't suddenly obsolete Max or Maya or XSI, but it might be the first rumblings of a big change in how things get done.  As a side benefit, it's got a several different file IO options and could perhaps make as decent waystation for people crossing product boundaries.

The TA who immediately asks "what scripting language" will be pleased to note that there's a script console down at the bottom; unsurprisingly for a web app the scripting language is JavaScript.  JS may not be my favorite language -- by a long shot-- but it's the natural language of the web. Most importantly, the scripting system makes it fairly easy to extend the application by writing your own plugins: this example on their website shows how to make an OBJ importer.

Most important of all, though -- did I mention this? -- it's a browser app. Which means you can so things like this:



Which, you must admit, scores pretty well on the Gee-Whiz scale.
Overall, this is a really excellent developmnt. The 3d market has been painfully sluggish for a decade now - we've got better bells and whistles but on the whole we still work in 2014 more or less the away we worked in 2004.  We need new companies with fresh ideas to shake up this market.  We also need to embrace web development as a part of how we build tools.  This is a big step in the right direction and we should do what we can to help it along.

Thursday, January 2, 2014

The sounds of (Python) Silence

After a long vacation with my children, I've been meditating on the virtues of silence.

Python is a glorious toybox bursting with fun gadgets to delight TA's near and far.  You can easily use it to stuff anything from database access to a serial port controller into your copy of Maya, which is a always fun (and occasionally useful).  However the plethora of Python libraries out there does bring with it a minor annoyance - if you grab something cool off the cheeseshop you don't know exactly how the author wants to communicate with users.  All too often you incorporate something useful into your Maya and suddenly your users have endless reams of debug printouts in their script listener -- info that might make sense to a coder or a sysadmin but which is just noise (or worse, slightly scary) for your artists.

If you're suffering from overly verbose external modules, you can get a little peace and quiet with this little snippet. The Silencer class is just a simple context manager that hijacks sys.stdout and sys.stderr into a pair of StringIO's that will just silently swallow any printouts that would otherwise go to the listener.


If you actually need to look at the spew you can just look at the contents of the out and error fields of the Silencer.   More commonly though you'll just want to wrap a particularly verbose bit of code in a with... as block to shut it up.  You'll also get the standard context manager behavior: an automatic restore in the event of an exception, etc.