Friday, June 11, 2010

It's Alive!!! idTech4 models with skinning.

So, not content to just show a static Spore creature I decided that my next project in WebGL was going to be something a bit more dynamic. After a little bit of toying around, I decided to give the MD5 mesh format (Doom 3, Quake 4, etc.) a try. I think it turned out pretty well...

Click here to run

(I hope Mr. Carmack and company don't mind me using one of their models to demo with. If anyone should take issue with it I'll remove that model and swap it out for another, but you've got to admit that seeing a Hellknight chilling out in a browser is pretty cool!)

[EDIT: Interleaved arrays have been fixed in the latest Minefield releases, which allows the demo to run correctly.]
As of right now I've confirmed that the above demo runs with Chrome, but not Minefield. I'm pretty certain it's because I'm using interleaved arrays, which is still horribly broken in Mozillas implementation last I heard. Your mileage may vary, and in any case I should have a more compatible version up in the next day or so.

I've worked with the format before, so I knew how it was laid out reasonably well and (crucially) I knew that the format was plain text. One of the biggest drawbacks to WebGL (and web development in general) is a near complete lack of ways to efficiently work with binary data outside of a select few media types. I must say, though, even given that the files were text based the actual parsing was far easier and faster than I expected. Certainly the file size and loading speed blows COLLADA out of the water. Guess that just a happy side effect of using a format intended for realtime rendering!

Of course, there are a few things that I know could be done better/faster that I might want to tackle in the future (and would love to hear about if anyone else improves on my methods!):

  • Material loading is a huge, ugly hack right now. Problem is that I can't figure out a happy method of loading the actual material definitions used by the game in a web-friendly way. So for the time being the loader simply looks for 'shader-name.png', 'shader-name_s.png', and 'shader-name_local.png' for the diffuse, specular, and normal maps respectively. It also ignores any special effects (hence the odd looking drool)
  • Skinning is done on the CPU right now, but I would like to try GPU skinning and see if there are any performance gains. In all honesty, though, the format isn't intended for GPU skinning and I'm quite pleased with the speed of the javascript based animations, so I'm not sure how necessary that is.
  • I'm not interpolating between frames at the moment. Playback is at 24 FPS, defined by the animation file. Certainly it would be nice to smooth that out a bit but I'm worried that the extra overhead would murder the framerates. It's worth playing with though.
  • I'm not 100% confident in my skinning methods of the normals and tangents. The results look correct enough, but may fall apart with more extreme movement. Also, I'm not taking mirrored uv's into account when calculating tangents (I think the models are built to account for this, but I could be wrong) and I'm ignoring texture seams when calculating the normals which leads to some sharper-than-I-would-like normal differences.
  • I'm hauling around a lot of unnecessary information after the initial load, and as such memory use could be much better.
  • Animating two independent instances of a mesh would require some changes as well, but wouldn't be too difficult.
Aside from those complaints, though, the library is reasonably fast and robust and should work with most of the MD5 meshes out there (something that certainly cannot be said about my COLLADA loader). It requires my glMatrix library, but is otherwise standalone.

I'd love to hear any comments or suggestions for improvement, and if anyone else builds something cool on top of the loader please let me know!