Monday, July 13, 2009

Animate a Face SOLD OUT!

The last copy of my Animate a Face CD set has been sold! I won't get a chance to update my site until later tonight, so if you know someone who's about to place an order, please pass along the news.

Even though the first-run CDs are gone, I still have a set for my own use, and am still keeping an eye out for alternate avenues of distribution. One distribution firm I was considering apparently doesn't have a system in place to sell multi-disc products. I haven't looked at the contents in a while, but there may be a way to compress it down to a single disc. Electronic distribution is an option that some have suggested, and I'd like to explore that if possible. Leads to appropriate distribution solutions are most welcome!

I'm very grateful to everyone who has taken a chance with my little tutorial product, and hope that it's been helpful in your animation efforts.

Monday, June 29, 2009

Two Webosaurs vignettes online!

We've been producing a number of short 15-second vignettes for Webosaurs, and the first couple have been released into the wild!

What I love about these pieces (not just these two, but the whole series) is that the animators at Reel FX were given a huge amount of ownership over their work. The team was given the task of creating mini-stories that could tie in with certain parts of the Webosaurs world. The group split into pairs, and each twosome developed a script, boarded and pitched it, directed the voice talent (Justin Harder and myself), animated the shots, and even offered direction on the background designs. It's been a huge collaborative effort, and the end result in each case is something that's part story, part ad, and all fun.

(Edit: word has it the crew here is going to update the clips so the ends don't get truncated by good ol' YouTube. They're a tad short in their current form.)

(Edit edit: fixed links to embedded videos, fixed size)

Wednesday, May 13, 2009

Kicking off voiceover site

I'm finally kicking off a separate voiceover web site:

Just a temp splash page for now, but the blog portion of the site is alive and kicking. Voiceover-related posts from this blog will gradually move over there. Many thanks to Grant Goodwin for help with the logo design.

Thursday, April 30, 2009

Short Story: The Cobweb

A little over a month ago, I took a day off to just relax and unwind. Somewhere in the middle of the day, the voiceover bug jumped up and bit me, and before I knew it I was perusing the available projects in the Librivox forum in search of something short to record.

It wasn't long before I located a thread for a collaborative production of Beasts and Super-Beasts. I'd been searching for material to use as practice for narrative voiceover work, and one of the unclaimed short stories in this collection caught my attention. After receiving word from the project coordinator that I could claim the requested story, I dove in. An hour later I uploaded the finished piece, which I present for your listening pleasure:

There are things that I could have done better, but all in all, I'm very happy with how it turned out. I hope you enjoy it!

Monday, April 06, 2009

Twitter me this...

I've semi-recently become a Twitter semi-junkie. As part of this semi-new-found addiction, I've added a Twitter gadget to the blog sidebar. Happy to hook up with other animators, developers, etc. However, be warned: I do weed my follower list from time to time, blocking users who (for lack of a better phrase) creep me out. If your creep quotient is low, you'll likely stay. :)

Wednesday, April 01, 2009

Totally awesome!

ThinkGeek always comes out with the best products. Especially at this time of year. :)

Sunday, March 29, 2009

Monsters vs. Aliens vs. Apathy

There are a only a handful of animated films that I have intentionally avoided for one reason or another. In some cases, the animation style was just too off-putting. In others, it was the apparent quality of the film as a whole that turned me off.

In the case of Monsters vs. Aliens, however, I simply didn't care. Not about the plot, and not about the characters. While I got a few minor chuckles when I watched the early trailers, nothing in them raised my interest to the point where I felt myself saying, "I have to see this!" In fact, the film was so far off my radar that I frequently found myself saying, "Oh yeah...I forgot that was coming out," when friends would ask about it.

And that's another thing: almost nobody in my animation circle was talking about it. It wasn't a hot topic like many past films had been, so those moments were friends would ask about the film were few and far between. Hence the forgetting.

Anyone else feel the same way? Chime in! Anyone see it? If so, what are your thoughts?

Friday, March 27, 2009

tweenMachine 2.03

tweenMachine 2.03 is published. It fixes a problem that has prevented Maya versions in the 200X series from using the special tick color feature. If you run into any issues, drop a note here (still haven't got the contact page on my site updated yet).

Thursday, March 26, 2009

Booleans, ternary operators, and max

I ran into a situation today that initially stumped me. I needed to take a list of unknown size, check each item in the list to see if it could be found in a line of text, and return a single True or False if any one item in the list matched that check. Oh, and if the list was empty, that had to be addressed appropriately as well. And I wanted to do it in the smallest way possible.

Originally I'd hacked together a short four or five line function that did the job. However, after coming back to the code for some other updates, I noticed that said function was only being called once, so I began looking for ways to nix that function and do the job more directly. Part of the challenge was that this comparison I needed to do was part of an existing if statement, and it had to remain so due to the way the rest of the code worked. In short, the comparison went like this (in pseudo-Python):

thisList = ["list", "of", "unknown", "length"]
if not ":" in line and not anythingInThisList in line:
do stuff
do other stuff

The first thing I tried to figure out was a way to get a single True or False out of a list of various Boolean values. On a whim, I tried to use the built-in max function. I'd used max before in standard numeric comparisons, but never with Booleans. Not surprisingly, it worked quite well.

>>> max(True, False)
>>> max([False, False, True, False])

Getting the necessary Boolean list to pass to max was a no-brainer thanks to Python's list comprehensions. Here's an example that quickly shows if a given word contains any vowels:

>>> vowels = ["a", "e", "i", "o", "u"]
>>> max([v in "sadness" for v in vowels])
>>> max([v in "shhhh" for v in vowels])

This process alone solved most of my problem. However, I couldn't always be sure that my list of things to look for would contain anything. The variable containing the list was set by an argument in a function definition, and that argument defaulted to None if no other data came through. That meant that I had to somehow force the output of the max comparison to False if the variable was None, or let it do its thing if not.

That got my thoughts going toward ternary operators. For those who don't know, many programming languages offer a condensed one-line shortcut for the standard if-else comparison, called a ternary operator. In short, it turns this:

if statement:

into this:

statement ? doThis : doThat

Not long ago I dug around to see if Python supported anything like this, and sure enough it does (as of one of the more recent versions, but I forget which one), although it isn't in the standard documentation from what I could see. At any rate, the above example would look like this using Python's approach to the ternary operator:

doThis if statement else doThat

With that applied to my max comparison, the end result looked something like this (and I hope this all ends up on a single line in the blog post):

# "line" is the line of text
# "y" is either None or a list of items to find in "line"

max([x in line for x in y] if y is not None else [False])

The reason that False needs to be encapsulated inside a list is because max needs a list of items through which to iterate, even if that list only contains a single item.

It's also possible to generate the Boolean list using the map function and a lambda, and the code isn't that much longer:

max(map(lambda x:x in line, y) if y is not None else [False])

Sneak peek at upcoming RFX project

I had the opportunity to work on a cool project at Reel FX last summer. We were awarded a contract to construct a series of sculptures to coincide with the opening of a new light rail station not far from the studio. Part of the prep phase involved posing some 3D models in Maya, with the resulting models to be used as templates for the final sculptures, and I had the honor of creating the needed poses under the watchful eye of Brandon Oldenburg, VP of Creative at Reel FX, who designed the installation.

Work is well underway on construction of the sculptures, although I can't give any more details at this time regarding what they'll be. However, if you happened to pop into Reel FX during the last month or two, you probably would have seen a work-in-progress version of one small portion of one sculpture sitting in our main atrium. If you didn't...I'm so sorry. Other than that, I can only say this: they will be BIG!

For more info, and a sneak peek at some of the smaller components of the installation (not to scale, in case you're wondering), check out the official site for the Deep Ellum Gateway Project. With the opening of the new station slated for September of this year, I imagine more info about the installation will come along any day now.

Friday, March 20, 2009


Just discovered a nifty add-on for Firefox: Yoono.  It sits in a thin sidebar in the main window, and lets me hook up to pretty much everything: chat, social sites, RSS feeds, etc.  I'm even writing and posting this blog entry from it.  Pretty slickaroonie, I must say.  :)

All right, enough geekin'.  For now, anyway. ;)

Wednesday, March 18, 2009

Changing styles, changing times

Imagine if the Merrie Melodies short "One Froggy Evening" were created by today's animation production system. It's very possible that it would turn out very differently, as one blogger speculates via a series of hypothetical notes that might come back from a review session with today's content critics.

What do you think? Do you think that the creation of such a film today is likely to generate that kind of response...that kind of over-analyzation of details that, in the end, really don't matter because it's all fantasy?

At some level, I feel that we are excessively critical with some of the things we create in the name of entertainment. Even in the realm of animation, we don't always allow ourselves to let go and let loose, to just let certain things be nonsensical and come out of left field. Why not?

Think about this: if that film were made as a live-action story (with a CG frog, naturally), matching the appropriate details of the animated version as closely as possible, how would we feel about it? Would we be as willing to accept the presented absurdities and disregard certain glossed-over details like we do with the animated presentation?

I don't know that there are any firm answers, but I've got some thoughts. It's late, though, and I need my beauty sleep (yeah, it's done me much good so far!). More postulating to come later...

Wednesday, February 25, 2009

Maya and Python lambdas, part 3

A thought occurred to me this morning before coming into work. I was still thinking that there had to be some way of further improving my use of lambdas in Maya GUI creation. Then it hit me: assign the data from the loop as a default value for one of the lambda's arguments!

import maya.cmds as mc

def showStuff (stuff):
print "You've given me %s!" % stuff

stuffList = ["an apple", "a pear", "a pickle"]
mc.window("Stuff to Give", w=300, h=200)
cl = columnLayout()
for item in stuffList:
mc.button(l=item, c=lambda x, i=item:showStuff(i))

Look, lambda factory! :) Here's my guess as to why this works when it doesn't work to put the variable "inside" the lambda...

By stuffing a variable into the function/method call inside the lambda function, it appears that the value of the variable isn't retrieved until the lambda function is executed. For a variable defined by a loop, this means that the variable value is the same as it was during the last iteration through the loop.

However, by taking a variable and assigning it as the default for one of the lambda's arguments, the value assigned to that variable is retrieved and stored in the lambda function definition. When called by Maya when the appropriate GUI element is used, the lambda function already has that value, and knows to pass it as the default for the appropriate argument. Because no other data is passed to replace it, it can be used reliably inside the function as part of the real function call we want to make.

The only thing to keep in mind when using this technique is the extra data that Maya passes on its own, which is caught (and promptly ignored) in the example above by the x argument.

Clean and simple. Me likey.

Tuesday, February 24, 2009

More Lambda and GUI fun

Just tripped over an interesting problem with the lambda stuff I shared in the last post. It's just peachy if you're passing a literal value:

# other code omitted for brevity
mc.button(l="click me", c=lambda x:colorMe("Purple"))

However, if you're creating a collection of controls using a loop, it no worky correctly:

names = ["me", "you", "him"]
for name in names:
mc.button(l=name, c=lambda x:nameMe(name)

In this example, no matter which button you click, it will pass "him".

I tried a number of ways to get around this, and wasn't successful until I revisited the page that flipped the lambda light switch for me. The second example on that page shows how to create a "lambda factory" of sorts. In the context of the loop situation, the factory serves to isolate the creation of the lambda function from the loop. While the factory function was a standalone item in the example on that page, it would be convenient to nest said factory inside the same function/method that contains the loop. Here's a more fleshed-out example:

import maya.cmds as mc

def addButtons (names):
# here's the factory function
def factory (nm):
return lambda x:showName(nm)

# and here's our loop
for name in names:
mc.button(l=name, c=factory(name))

It's a bit more extra code than I'd hoped for, but it's only needed when using the lambda technique inside a loop, and still allows me to keep the target functions/methods clean by avoiding nesting.

Monday, February 23, 2009

Python Lambdas and Maya GUIs

I love epiphanies. :)

For some reason, I haven't been able to figure out "lambda" functions in Python. Granted, I've not put a great deal of time into them. It's just that whenever I would see them in someone else's code, I couldn't immediately figure out what they were doing, so I'd just add another mark next to the "Need to research lambdas" entry in my mental to-do list and move along. To make a long story short, I ran across a page today that flipped the light switch on lambdas. But that's not the epiphany of which I speak. The epiphany hit when I began trying to figure out what (if anything) I could do with those lovely little lambdas in my Python programming at work.

Most of my development work involves the creation of tools with some kind of graphical user interface (GUI). When assigning a command to a Maya GUI element in Python, it expects either a string that contains some Python code to execute, or a pointer to a function or method that will be called. In the vast majority of situations, I'll use the latter option. If I don't need to pass any data to the target function, there's no problem, and Maya gets the function pointer as expected:

import maya.cmds as mc

def blah(*args):
# "*args" is required because even though the
# command doesn't pass any data, Maya passes
# some anyway. Go figure...
print "You touched me!"

mc.window(w=500, h=500)
mc.button(l="Touch!", c=blah)

However, in most of the GUIs that I create, some control will need to pass specific data to the function that it calls. The problem is that once you include parentheses to pass data to the function, Maya is no longer getting a function pointer. It's getting the value (if any) returned by the called function, or None if the function doesn't return anything.

Up until now, I've been using nested functions to get around this problem. By defining and returning a "dummy" function inside the main function that is called by the GUI element, Maya will get the function pointer it wants, and I can pass in any data that I please:

import maya.cmds as mc

def blah(value):
def b(*args):
# "*args" is still required because this is the
# function that Maya will ultimately call when
# the button is pushed
print "I was given:", value
return b

mc.window(w=500, h=500)
mc.button(l="Touch!", c=blah(10))

This process has been working fine, but in the back of my mind, I kept hoping to find a more elegant solution.

Enter my new friend: the lambda!

After some experimentation, I learned two very helpful things about lambdas. The first is that the expression evaluated by a lambda doesn't necessarily have to have any connection to the data it is passed. For example, a "normal" lambda definition might look something like this:

x = lambda y: y * 2

In this case, calling x(5) will return 10 (the 5 gets passed to y, when is then evaluated through the expression y*2 to yield 10, which is then returned). However, the expression can be changed to return something that has nothing to do with the value passed in through y, like so:

x = lambda y: 15

In this case, no matter what value you pass, 15 will always be returned.

Based on this example alone, I can already begin to use lambdas to simplify my example code above. (It's simpler on the function definition side of things because we get rid of the nesting issue, but some might see the syntax of assigning the desired function call to the GUI element a tad more confusing.)

import maya.cmds as mc

def blah(value):
print "I was given:", value

mc.window(w=500, h=500)
mc.button(l="Touch!", c=lambda x:blah(10))

What happens is that the mystery-data passed by Maya gets assigned to x in the lambda definition, but we don't need to use it. All we need is to call our function with the desired value.

For some GUI elements, though, the data passed by Maya is actually useful. Take an intSlider, for example:
import maya.cmds as mc

def blah(value):
print "The slider value is", value

mc.window(w=500, h=500)
mc.intSlider(dragCommand=lambda x:blah(int(x)))

In this situation, the data passed by Maya when dragging the slider is the slider's value. However, it's passed as a Unicode string, so I just converted it to an integer before passing it to the function. This means I don't have to query the slider, as the data I need has already been passed.

Some GUI operations, like dragging and dropping, pass more than one argument to the target function. No matter...just provide the requisite number of arguments in the lambda definition (i.e. lambda w,x,y,z: ....) and pass them along to the target function as desired. Or, as in one particular case where I wanted to substitute my own data in place of what the drag operation passed, you can take advantage of the other nifty thing I learned through my experiments: a lambda can accept arbitrary argument lists, just like normal functions.

# other GUI code here
mc.button(l="blah, dragCallback=lambda *x:boo(myData))

That's all for now. Happy Python GUI building!

Monday, February 16, 2009

Hardware woes, begone!

Okay, I think I've finally finished fiddling with this finicky figurer (the only synonym for "computer" that I could find that started with F).

After my last post, I ran across some other info that said that the power supply might be to blame, and I began seeing other evidence to support that theory. Shortly thereafter the computer just refused to boot, so I had to do something. With the exception of the first item below, most of my attempted somethings were done this past Saturday...
  • Bought a beefier power supply. Still nothing.
  • Bought a new motherboard. Got it home and found that it didn't have enough slots to take all my existing RAM sticks.
  • Exchanged the motherboard for a newer one that required a new CPU (picked a nice energy-efficient dual-core). Got home and found that the location of all the externals (keyboard and audio hookups, plus the location of the PCI slots) wouldn't work with my case.
  • Trekked to Best Buy for a new case. I was tired of messing with hardware, so I paid them to move all the guts from the old box to the new one while we went and had a nice Valentine's Day family dinner.
  • Came back a few hours later to find that my old RAM wouldn't work with the new motherboard, and that the board only had one IDE connector, so the DVD drive had no place to connect. Bought new RAM and a new SATA DVD drive, and had the Geek Squad install both. Brought everything home.
  • Nothing booted. Found that the hard drives were connected in the wrong order. Had to swap their positions in the case because of the awkwardness of the IDE cable.
  • Found that the DVD drive wasn't being recognized because it was connected to the wrong SATA connector on the motherboard.
  • Booted into BIOS, set everything up, then started to boot into Windows. Had to re-authorize Windows, which ended up taking several phone calls to MS. Thankfully I brought the old machine home, or I would not have had access to the product key label that was conveniently stuck to the back of the case.
Based on information that I read in some of the articles I found online after the last post, I thought that I would have to run a repair install of Windows in order to get it to play nice with the new hardware. Once the re-authorization was finished, though, Windows booted up without any issues and just started bugging me about all the new hardware it was finding. I haven't had any BSOD's or any noticeable system hiccups.

In the end, all this hardware futzing cost about the same as (or more likely more than) a new MAChine (*ahem*), and in the end I wound up with essentially a new machine, so I guess it kinda served its purpose. Our key concern was getting access to all the data on my hard drives again (especially financial data). It also got us thinking about where we want some of said data to reside for better long-term, computer-independent access.

So there we go! Done!

I hope!

Monday, January 05, 2009

Most definitely NOT the monitor

In the ongoing fight to get my desktop system fully functional again, I feel I've pretty much eliminated monitor problems from the equation. I just finished two quick tests. I moved the monitor to my wife's machine and hooked it up via the DVI input. No problem. I then moved it back to my machine, pulled out the new graphics card, and ran the motherboard's on-board VGA output to the monitor's VGA input. No problem. In fact, I'm using that VGA hookup now, which is the first time I've been able to use the desktop for the past several days.

All signs at this point are aiming squarely at the motherboard, and specifically the PCI Express slot in which the video card sits. The rest of the board seems to be working fine, as my presence here (hopefully) indicates. While it's nice to have a fairly solid target at which to direct my next efforts, it's not the target I wanted. If it were the monitor or video card, it wouldn't be much of an issue. Get a new one, plug it in. With the video card, there would be drivers to install, but that's still a piece of cake compared to the work involved when swapping motherboards.

A new board typically means reinstalling Windows from scratch. At least that's what it used to mean. However, I just found a couple web pages that have detailed instructions on how to swap motherboards without affecting Windows. It's still more work than installing a new video card or monitor, but much less work than a full re-install. we go!

Sunday, January 04, 2009

Curse on Apple lifted....for now

Wouldn't you know it....I found a solution not five minutes after that last post:

Import the photos into iPhoto. Then go back to iMovie and drag the photos in from the iPhoto library.

But seriously...that's still a few steps too many. They should "just work" (famous last words, Apple) when dragging the photos in from the Finder, no?

The day I curse Apple

That day is today.

I don't know why they did this, but in iMovie HD, all vertically-oriented photos get rotated back to horizontal orientation, and there is NO way provided in the software to rotate them back.

Sure, I could import them into a photo editor, lay them over a horizontal black background, save them out, then pull them back into iMovie....but come on. I just wanna make a stinkin' slideshow with some videos intercut here and there. What happened to the ease of use that they keep touting in all their ads?

I could've sworn that I used some vertical shots in a movie project a while ago, but for the life of me I can't remember how/if I pulled it off, and an hour (or more) of Googling has not yet led me to a solution.

That said, the evening wasn't a total waste. I did manage to develop a headache! Thanks, Apple!

Saturday, January 03, 2009

Third time's a (very frustrating) charm

This is just getting more annoying all the time. My initial tests led me to think it was the monitor, but later I was able to get the monitor to work with the laptop, so I thought it had to be the video card. Now I've got a brand new video card, but I still have no image from the desktop machine, and the laptop still works just fine with it. So where does that take me next? The motherboard. Ugh...

I think I'm going to see if I can get the motherboard's on-board video to work. If so, then my guess is that it would mean that something tied to the PCI Express slot is messed up.

However, that still doesn't explain why my wife's monitor worked fine with my computer in one of my earliest tests. I think I'll try that one again as well. If that still works....well, I'll be completely stumped.

NOT the monitor!

Looks like I typed that last entry too soon. While things worked okay last night, I got absolutely no signal this morning. After more cable wiggling, more detaching and reattaching, and more under-the-desk spelunking, I decided to try plugging my Mac laptop into the monitor. Bingo! Beautiful picture...and it was via the DVI input. After all the other tests, that could only mean one thing:

I've got a dead video card.

Oh joy....

(BTW...absolutely meaningless bonus points for those who can decipher the slightly cryptic reference in this post's title. Think early 90's TV...)

Friday, January 02, 2009

Minor Monitor Mayhem

I seem to have this thing with monitors. About a year-and-a-half ago, my trusty CRT died on me in the middle of a live Q&A with my Animation Mentor class. That paved the way for the purchase of the current widescreen LCD monitor, which was humming along just fine...up until this morning.

I powered the computer on, fiddled around for a half-hour or so, then hit the sleep button on the keyboard before heading off to shower. Upon returning a little later, I hit the space bar on the keyboard to wake up the machine. The computer woke up, but the monitor didn't.

At first I thought it might just be a really long delay while the system was processing something before it would fire up the video signal. It had occasionally behaved that way in the past, but after a minute or so of no picture, I figured something else was amiss, so I started testing things. Cable wiggling got me nowhere. Borrowing my wife's monitor (identical to mine...the result of one of those "as long as you're replacing yours, would you mind getting a new one for me?" requests) and plugging it into my box gave me a good picture. That told me that the video card and cable were probably fine, but that the DVI input on my monitor was probably dead, so after work I bought the necessary accoutrements to use the SVGA input, and it doth work again. Yea, verily! Glad to know it's not a total goner. The SVGA signal doesn't look horrible, but it is a tad blurry. Still, I'll gladly sacrifice a little clarity in order to save some dough.

I hope everyone had a great Christmas, and wish you all the best in 2009!