Now with 30% more monsters.
Update time :cap:
Been trying to solve the problem of how to sort monsters/items and other dynamic entities into the map. When the map consists of just the convex sectors & the portals between them (like descent), this is easy to do. As every monster polygon is drawn, just clip it against the portal that exposed the sector where the monster resides. No other polygons of the sector can occlude the monster, so draw them first and then the (clipped) monster goes back on top.
But when you permit the sectors to contain solid brushes that fill back in the space (like pillars, floating platforms, and other interesting geometrical bits) you have a real mess because these things can occlude the monster in complicated (possibly cyclical) ways. The brushes themselves will properly sort amongst themselves when you carve them up with a BSP, but this is only tractable when you can do the splitting upfront and store the cuts. If you have a brush that moves (like a menacing, roaming tetrahedron), this cutting process must be done every frame. I did implement that approach first, but it's far too slow to do in realtime. Flash is more polygon limited than fillrate limited, and this approach does not play to that economy. The same number of pixels are drawn, but you're drawing lots of little triangles and dramatically inflating your number of polygons because you split one monster polygon into 5-10 fragments depending on the scence.
The other option I see is zbuffering the map scene, and then doing conditional pixel writes when you draw in all the monsters in a second pass. This is attractive because it plays to the fillrate/polycount economy. Filling the zbuffer doubles your polycount and pixel fill requirements, but you have plenty of the latter already and it's still lower polycount than the previous case. I hoped to use some kind of pixelbender routine to do the compositing, but (!) Pixelbender is not at all performant compared to existing bitmapData methods, at least not in its current implementation (!).
So if you want to zbuffer quickly, you have to implement this test & write using the existing methods on BitmapData. I haven't figured out how to do this on a pixel by pixel basis right as each monster pixel is being drawn, but I think a workable compromise is to draw all the monsters (and their z) back to front. Then you have four separate graphics contexts with (i) map-texture (ii) map-z (iii) monster-texture (iv) monster-z. Actually it's a 1/z buffer with 8 bits of resolution, which can be drawn very quickly using gradient fill because 1/z is linear in screen space and can be swept using the 8 blue bits. These four layers can be composited very quickly by:
[1] draw the monster texture layer into BMPD1
[2] draw the map texture layer into BMPD2
[3] draw the map 1/z-buffer into BMPD3
[4] draw the monster 1/z-buffer into BMPD3 using Blendmode.SUBTRACT // clever step #1
The effect? Wherever the monster 1/z-buffer is greater than the map 1/z-buffer, this clamps to a zero in the blue channel. That is, when the monster occludes the map, we get a zero. Otherwise, the map occludes the monster.
[5] do a threshold operation:
BMPD2.threshold( BMPD3, "==", 0x00000000, 0x00000000, 0x000000FF, false);
Threshold is a strange function, it took me a lot of time to figure this part out. What is happening here is we are searching for the zeros of the blue channel of BMPD3, and using it to blank out pixels in the map texture, by alphaing them to zero.
[6] do a draw operation (or copypixels, take your pick)
BMPD1.draw (BMPD2).
This overwrites the monster texture layer with map pixels, _except_ where they have alpha=0. So we've basically made cutouts in the map layer than now expose the monster pixels.
[7] BMPD2 contains the finished frame - blit it to screen however you like.
Other than the fact that it's only 8 bits, it works pretty nicely. It's way quicker than pixel bender and its quite a bit faster than any other bitmapData approach I tried. (compare, paletteMap, and copychannels are pretty sluggish compared to draw, beginBitmapFill and copyPixels). The 8 bit limitation comes from the Blendmode.SUBTRACT step, because it's clamping semantics won't work across channels. It might be possible to extend this scheme to slightly more precision, but probably it will cost more to composite.
Anyway, enough talk and on to the goods. Here's the first monster I ported in, he looks kinda familiar.
http://esl.eng.ohio-state.edu/~rac/quake/roboman.gif
Here's what the zbuffers look like (monster zbuffer, map zbuffer, result of BlendMode.SUBTRACT, and final image).
http://esl.eng.ohio-state.edu/~rac/quake/zbuffer.jpg
And here's what the final image looks like, once you've blown it up by 2 to fill the screen (800x600).
http://esl.eng.ohio-state.edu/~rac/quake/final.jpg
To summarize the sorting properties of this code:
1. Maps sort flawlessly using a combination of portal rendering and local BSP's between the brushes that exist in a common cell.
2. Monsters self-occlude/sort against themselves flawlessly, because they're BSPs too. Even when monsters are articulated/moving, you can still accomplish this by storing each frame of the walkcycle as a separate mesh and store them as distinct models.
3. Monsters sort pretty well against the map, the only problem is a lack of zbuffer resolution but this can be managed somewhat through careful map design.
3. (The only negative) Interpenetrating monsters do not sort 100% correctly. This is because they don't get z-composited individually, they get drawn back-to-front into a single buffer and then the whole stacked mess gets z-composited against the map in that 7-pass scheme. In practice, this will not be noticed very often. (Pinball Racers did a similar back to front step and sorting errors are hard to spot when you have sparse and separated geometry).
Here's a demo to play with (finally, right?). Shooting for 20 frames per second, and I think my computer is weaker than some of the others around here, so it might be quite speedy on some platforms. This can become a game for sure :) - it looks pretty and plays decent.
http://esl.eng.ohio-state.edu/~rac/quake/zbufdemo.zip
You'll need the flash10 debug player (or maybe the plugin) to play it. I am gunshy about installing the newest plugin, so I just use the standalone debug player available at:
http://opensource.adobe.com/svn/open.../in/player/10/
(that way you don't have to clobber your system just trying to run 1 demo)
THANKS FOR READING!
(...and super thanks if you try playing it and post a FPS quote! If you do post a quote, try to use the same spot where I screenied from, because scene complexity really matters and that's the worst case)