Saturday, July 12, 2008

Locators: Draw vs. Compute





Suppose you the studio tools programmer at XYZ Animation, want to create a custom locator inside of Maya. Your custom locator is supposed to draw something using OpenGL. However the locator need to perform specific drawing based on input data (ie a deformable surface). A concrete example might be to grow blades of grass on an animated character mesh (a grass monster). Your locator takes as input a mesh and density. The locator draws blades of grass on the mesh. The motivation behind this example is that you want to previz some effect that not will happen until render time - you're going to have PRMan do the grass drawing. For now, we want to get rough feedback for an animator so they can see roughly what's happening in Maya.

The solution to this is to create an MPxLocator node. The locator will have at least two custom attributes as input (surface and density). The locator's draw function will perform OpenGL drawing. The draw function will need the points and orientations on the surface where grass drawing will be done. Simple enough, right? In the locator's Draw() function, the locator could read in the surface and density, and regrow the grass. However, what if the grass growing process is slow? Every time the locator draws (ie you pan the camera), the locator is performing a ton of drawing work. OpenGL display lists aren't going to help because the surface is animated. It would be best if the hair growing was done only when either the surface or density changes.

The solution: Maya's dependency graph. Put the grass growing inside the locator's compute function.




If you are familiar with Maya's dependency graph, you can safely skip this section. If not, read on:

Maya uses a dependency graph to manage efficient computation. The idea behind the dependency graph is to create a world with connected nodes that only need to perform computation when necessary. A node only needs to perform computation when it's outgoing data is needed and it's incoming data has changed. This is accomplished using a dependency graph structure that utilizes a notion of "dirty flags". If a node's input data is "dirty", then the node needs to re-read that input data, perform some computation, and update it's output data.

If this is all new to you, please consider taking a look at David Gould's Maya Programming sections 2.2. It's a bit confusing at first, but David has done about as good a job as any mortal could in explaining a 3rd party system that's a bit of a black box.



Now to our concrete example. Go ahead and download the Grass Locator C++ example from the mayaprogramming google code site. Alternatively you can checkout code via svn:


svn checkout http://mayaprogramming.googlecode.com/svn/trunk/ mayaprogramming-read-only



Now that you have the GrassLocator project open in Visual Studio, take a look at the GrassLocator node. The key idea behind this example is Draw() getting Compute() to do it's 'dirty work'. This is a key point, and a terrible pun. Compute() utilizes dirty flags to minimize computation.

The drawing process is as follows:

  1. OpenGL decides it's time for the GrassLocator node to draw itself, GrassLocator::Draw() is called.
  2. Draw() needs to know what blades of grass to draw where.
  3. Draw() could read in the mesh, generate points to grow at, and grow the grass. However that would be slow (growing every time GL refreshes).
  4. Instead, Draw() queries it's own sentinelAttr.
  5. Once Draw has queried sentinelAttr, Maya can check with the dependency graph - is there something that affects sentinelAttr (ie surfaceAttr, densityAttr,...) that has changed? If the surface has changed (ie it is skinned and attached to a joint that just moved), then the request for sentinel's data results in a call to GrassLocator::Compute(). (See the pic at the bottom of the page for an example of what I'm talking about as far as a deforming surface.)
  6. Compute() updates the GrassNode::lawn member
  7. Draw() can safely use the GrassNode::lawn member - each GrassBlade in GrassNode::lawn is drawn by OpenGL.



A few caveats from this example: Maya does not provide a simple way to get a point at a random UV location on a surface. It does allow a random UV on a face. One could use a KD-Tree to find nearest points in UV space as a work around. However, that's going to bulk up this code and is a bit outside the scope of this example. In a later example I may go ahead and put in a nice mechanism for getting points at UV locations. For now we're stuck with guessing UV's and see if they land on a face - not terribly efficient. Maya does have a number point-at-UV examples in their API - they're limited.

A second caveat - I use a display list mechanism. The Sentinel returns a flag that can be compared to class dList flag. This indicates to Draw() that the display list needs to be regenerated.

As well, the drawing code could be improved - normal interpolation, better faux-droop. Again, this is a general example, not a grass/hair/fur system. The big idea as far as a "Grass system" goes, however, is that instead of having an elaborate drawing routine in Maya, there would be a shared chunk of code that both Maya and PRMan would use to draw grass. The faceIterator/UV issue makes this a bit trickier.




The above image shows the fur grown on a surface deformed by a joint. Without Compute() handling efficient computation, this would be impossible to draw in real-time.


That's about it. Go ahead and play around with the code. I've put it there for you to use. This example was intended to build on the Locator examples from Rob the Bloke. If you have questions or suggestions, please comment or email me.

3 comments:

Vishang Shah said...

Hey, Liked your initiative on Maya API programming, as there is very less info on the subject, I am Technical Artist at Electronic Arts, also interested in CG Programming, visit http://shahvishang.blogspot.com/ if you like, cheers

niic said...

Hi,

Great stuff. Are you able to render out your custom locators via the Hardware Render Buffer?? Very keen to know this!!

Cheers N.

ufukmachamer said...

The Best 10 Casinos Near Bryson City, Bryson City - Mapyro
진주 출장마사지 casinos › Bryson › casinos › Bryson Bryson Bryson City has an open and 전주 출장샵 spacious casino floor with slot machines and table games. The 인천광역 출장안마 hotel offers more 구미 출장샵 than 70 table games, many live shows, 화성 출장샵