Monday, October 31, 2011

Softbody solver: first test



This is an early test render from my slow-moving pet project, a softbody dynamics solver for Maya. It's based on the core technology from the hair solver I wrote for "Tron Legacy", with added support for conversion of arbitrary poly surfaces into tet meshes using TetGen.

I'm still working on extracting the resulting surface so that I can plug it into a proper renderer. Meanwhile, please enjoy the OpenGL framegrab above. It shows a tetrahedralized poly sphere which is affected by gravity and a couple of kinematic poly meshes which are represented as level sets internally (for faster and more robust collision lookups).

Due to the large amount of proprietary Digital Domain libraries involved in the solver, this is strictly an in-house project which will most likely never see the outside of DD's facilities. Hopefully I'll be able to use this to generate some interesting animation down the road though, even if it's just to satisfy my own curiousness.

The following credits are due:



Thursday, October 13, 2011

Maya 2012 idle event issue

This might be old news to some but I just discovered another baffling "feature" of Maya 2012. Try this in the Python editor of a Maya session you can do without:

import maya.utils
def foo():
  print 'here'
  maya.utils.processIdleEvents()
maya.utils.executeDeferred( 'foo()' )

Boom! Infinite recursion, stack blows up, segfault, good night.

So what's going on here? Well, it seems that somehow processIdleEvents() doesn't actually pop the event queue until after it's processed an event. Call me old fashioned but isn't that kinda backwards? Wouldn't you pop the queue right after you acquire the event, just to prevent issues like this?

The reason I came across this in the first place is we had a case of someone launching a Python script from MEL using evalDeferred, in order to avoid these massive UI deadlocks that would regularly happen due to some unfortunate combination of Maya 2009, Linux, pyQt and context menus. Now, deep down in one of our Python modules someone else had added a call to processIdleEvents() in order to guarantee that a loadPlugin() call has completed before proceeding - most probably due to some other synchronization issue in the Maya Python engine. You can guess what happened next, to everyone's stunned amazement: the script simply restarted, without any clue whatsoever why that might be.

The way I was able to catch this was by using 'traceback' to print out the call stack at a certain place in a module that I happened to have control over, realizing where the jump was happening. The solution? I'm not quite sure yet but it seems as though the UI deadlocks that gave rise to the use of evalDeferred in the first place have been mitigated post Maya 2009, so hopefully by just using a straight-up python() call instead we can put this behind us. However, this somewhat unorthodox treatment of idle events seems as something that should at least be mentioned in the API docs, if not fixed, period.

Or did I get any or all of this backwards? Please leave a comment if you have any thoughts on the matter!

Wednesday, October 12, 2011

Python list comprehension

I've written about Python list magic in the past and here's another tip:

Say you wanna create a list based on some other list, possibly filtering out certain entries and/or modifying the ones you choose to include.

Rather than this:

result = []
for item in mylist:
  if item > 5:
    result.append( '%05d'%item )

give this a go next time:

result = [ '%05d'%item for item in mylist if item>5 ]

Pretty neat, huh? Obviously this works on all valid sources of list data:

print 'Modules with \'site\' in the name:\n%s'%'\n'.join( \
      ['%s : %s'%(name,module) for name,module in \
        sys.modules.iteritems() if  'site' in name] )

Once we know this pattern it's fairly straightforward to create nested versions thereof, like this one straight out of a Maya script:

for attr in \
    [ '%s%s'%(attr,dim) for attr in ['t','r','s'] \ 
                         for dim in ['x','y','z'] ]:
  maya.cmds.connectAttr('%s.%s'%(sourceNode,attr), \
                        '%s.%s'%(targetNode,attr), f=True)

which combines two lists into one sequence of Maya attributes ['tx','ty','tz','rx','ry'... ] to connect between two nodes.

Expressive and neat, just the way I like it.

Wednesday, September 07, 2011

Google Test

As usual Google gets the behind-the-scenes stuff just right with their "C++ Testing Framework":

http://code.google.com/p/googletest/

For the simplest possible usage example, just include gtest/gtest.h and add this to your application entry point:

::testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();

Now, each test you wanna add is declared like so:

TEST(TestCase,TestName)
{
   // test goes here
}

The framework provides a bunch of macros for value tests and assertions, a very simple example being:

ASSERT_EQ( 1, someValue );

where the test fails and spits out an instructive error message in case someValue != 1.

I've found this framework to be extremely simple to learn, but once you scratch the surface you'll find it also has quite a few advanced features such as listeners, reflection, type-parametrized testing etc. to keep you busy for quite some time.

Friday, May 27, 2011

Eigen - versatile, fast, reliable linear algebra library for C++

After years of trudging along with all manners of legacy libraries for vector math, both homegrown and 3rd party, I finally got turned on to Eigen by a coworker. Unfortunately I can't provide any first impressions of it yet - I've been too giddy over all the cool features (vectorization! loop unrolling! lazy evaluation! elegance! it's free!) to actually be able to sit down and try it out. Instead, I present you with the product overview verbatim from their website:

  • Eigen is versatile.
    • It supports all matrix sizes, from small fixed-size matrices to arbitrarily large dense matrices, and even sparse matrices.
    • It supports all standard numeric types, including std::complex, integers, and is easily extensible to custom numeric types.
    • It supports various matrix decompositions and geometry features.
    • Its ecosystem of unsupported modules provides many specialized features such as non-linear optimization, matrix functions, a polynomial solver, FFT, and much more.
  • Eigen is fast.
    • Expression templates allow to intelligently remove temporaries and enable lazy evaluation, when that is appropriate.
    • Explicit vectorization is performed for SSE 2/3/4, ARM NEON, and AltiVec instruction sets, with graceful fallback to non-vectorized code.
    • Fixed-size matrices are fully optimized: dynamic memory allocation is avoided, and the loops are unrolled when that makes sense.
    • For large matrices, special attention is paid to cache-friendliness.
  • Eigen is reliable.
    • Algorithms are carefully selected for reliability. Reliability trade-offs are clearly documented and extremely safe decompositions are available.
    • Eigen is thoroughly tested through its own test suite (over 500 executables), the standard BLAS test suite, and parts of the LAPACK test suite.
  • Eigen is elegant.
    • The API is extremely clean and expressive while feeling natural to C++ programmers, thanks to expression templates.
    • Implementing an algorithm on top of Eigen feels like just copying pseudocode.
  • Eigen has good compiler support as we run our test suite against many compilers to guarantee reliability and work around any compiler bugs. Eigen also is standard C++98 and maintains very reasonable compilation times.