10/20/2008 - This was harder than you'd think, but easier than I thought:

Spacetimewar's Transparent Text

What was the trick? Well, what FreeType actually rasterizes and what I want to draw on the screen are very different. FreeType, being a general-purpose font rasterization library, consumes vector fonts and produces bitmaps for a given font size. (Actually, what it produces is even better. It produces "spans", of the form "Beginning at X = 4, Y = 7, there is a horizontal span of 137 pixels with 255 coverage", where "255 coverage" means "fully opaque". Spans can be easily used to produce bitmaps, and they can also be easily used to gather various metrics, such as the size of each character glyph.) FreeType is extra awesome in that it contains a "stroker", which can generate borders for font outlines.

After rasterization, each character glyph, which I call the "core", looks like what I draw on the screen. (Because it consists of coverage information, it doesn't have a color, but it can be easily painted with a color, even a gradient, as I have done.) When using a stroker, rasterization emits a second bitmap (actually spans, as before), which I call the "border". However, the border actually covers the area of the core, plus a little around the edges.

Now that we've gotten the core and border bitmaps, what does it take to actually draw something on the screen? Rendering text efficiently (actually, rendering anything efficiently) involves arranging things so that the video card does the heavy lifting. Today's video cards are insanely powerful, especially for 2D graphics, but you have to use them properly. Drawing an image on the screen is as simple as loading it into a texture and drawing a textured quadrilateral (this is what I've been doing for over a year with the various JPEGs and PNGs in my screenshots, such as the background starfield). Similarly, rendering text efficiently means using textures. For simplicity, it's easy to imagine creating one texture per character, and rendering textured quad after textured quad with different textures. That works, but it's slow - switching textures is expensive for video cards to do, when you're doing it for every character in a string. Instead, if you create one texture for the whole font (often called a "mosaic texture"), and render every character in the alphabet (and all of the symbols) into that texture, then you can texture quads with subregions of the master texture. Video cards really like that, and reward you with speed.

Imagine that you're drawing character after character in this manner. You've got a problem - characters can overlap, especially when you've given them fancy borders. But in the string MEOW, we don't want the E's border to cover up any part of the M's core. We want the borders for all characters to be behind the cores for all characters. Therefore, we need to render text in two passes: first, a set of textured quads consisting of MEOW's borders, and then another set of textured quads on top consisting of MEOW's cores. That solves the overlapping problem (it's okay when borders overlap borders or cores overlap cores, since they're the same color - this is true even for gradient coloring). It's not really more expensive, either - you're rendering twice as many quads, but that's really cheap. You're still using a single mosaic texture, and simply retrieving the border and core information from it separately. (I'm skipping over the details of how this is done - in particular, vertex shaders and pixel shaders are doing the heavy lifting here.)

So that's awesome. Now what's the problem? Well, if you're really hardcore, you won't be satisfied with perfectly antialiased, gradient colored, bordered (and separately gradient colored) text. You want more: transparent text. However, if you make your cores transparent, your terrible secret will be revealed: you've got borders underneath. Instead, you want to preserve the illusion that the borders surround the cores. (Why not simply stamp the core on top of the border of each character glyph? Remember, overlapping.)

In the end, I solved this by using depth buffering. Yes, depth buffering, in my 2D game which previously had no use for it. It turns out that pixel shaders can emit depth values, so instead of drawing the borders and then the cores, you can draw the cores and then the borders. While drawing the cores, you emit close depth values. While drawing the borders, you emit far depth values. In that manner, the borders won't overdraw any pixels covered by the cores. Then you do something sneaky in order to deal with core pixels that have partial coverage (these occur on the antialiased edges of the core glyph), because otherwise you'd draw a partial core pixel without any border, leading to ugly gaps.

Finally, you go back and fix borderless text (which you broke by making your calculations assume that they could divide by certain border metrics, leading to division by zero), and you fine-tune your calculations to make pixel and subpixel-wide borders work. And you (or I) get this:

Zero, Half, And One Pixel Borders

Subpixel borders are a special challenge, because they lead to pixels that are partially covered by the core, partially covered by the (very thin) border, and partially uncovered. With larger borders, you can assume that pixels partially covered by the core are otherwise fully covered by the border. Here's everything put together:

Version 2.0.1.0 of libnuwen adds sequence container typedefs for float and double , and removes an obnoxious dependency on Boost.Lambda.

These dual updates can mean only one thing: I've been working on Spacetimewar!

Spacetimewar Using FreeType

I've rendered this screenshot without antialiasing in order to demonstrate an important point: rendering fonts with FreeType instead of with polygons means that Spacetimewar's text will be antialiased regardless of whether your video driver is antialiasing polygons. It gets better: FreeType's antialiasing is perfect, because it's mathematically computing exact pixel coverage (through black magic that I'm glad I didn't have to write). Polygon antialiasing doesn't pretend to be mathematically exact. And yes, this is all being rendered at 372 frames per second. Note that instead of timing how long it takes to render a single frame and then taking the inverse of that, I simply count how many frames I've rendered in the last 2 seconds and divide by 2.

Also, FreeType allows me to draw borders around text, which massively enhances its readability against busy backgrounds. This was fundamentally impossible when I was using polygonal text (without resorting to completely disgusting hacks).

What remains to be done, before I actually start working on the game itself? You'll notice that I haven't reimplemented gradient coloring on text yet, or any coloring whatsoever. I'll be doing this with pixel shaders. In fact, I'm already using pixel shaders to render all text, so extending them will be easy. There's also some work that I have to do in order to accomodate different resolutions, because not everyone runs 1920x1200 like I do. Spacetimewar is a resolution-independent 2D game, as I hate fixed-resolution 2D games with a burning fury. (You'll get letterboxing or pillarboxing if your monitor doesn't have a 16:10 aspect ratio; distorting aspect ratios is thoughtcrime.) Since I've carefully designed the engine to be resolution-independent, all that I need to do is avoid graphics that'll become obnoxiously tiny or horribly aliased at my minimum supported resolution. However, pixel-perfect graphics are an exception. One example is the cursor (I'm still deciding what to do about that; I'm not happy with its current implementation), and the other example is text rendered with FreeType. Such text can't be resized or translated to non-pixel boundaries without significantly degrading its quality. Therefore, I'll have to write a special codepath that deals with non-1920x1200 resolutions. (Internally, Spacetimewar renders to a "virtual" resolution of 1920x1200; I'm going to have to place UI elements and the like somewhere, and I may as well do it according to my own monitor's coordinates.)

I haven't had time to review any SF novels as I had planned; the beginning of this month was completely annihilated by having to fly to Colorado for a week due to a medical emergency (not mine). And as for the rest of the month, whenever I'm inspired to work on Spacetimewar, that supersedes everything else.

8/16/2008 - And by "tomorrow", I meant "this weekend". You are in a maze of twisty updates, all different:

I updated the Links page, adding a few new links and fixing a broken one.

I replaced all of my old jumptables, even on pages that I'm no longer updating. (Jumptables are the things immediately below the site directory on some pages.) This doesn't really modernize my old pages, but at least it makes them look slightly less hideous.

I removed some insanely stale sections from the Archived News page; all that remains are the news posts themselves.

I removed my 6 ancient essays. These were relics of a time when I was still trying to imitate my pre-2000 site by writing about whatever came to mind.

I removed the Parrises Squares page. I released that game over 5 years ago, and its source code had become actively harmful to the uninformed. (I was, however, able to salvage its OpenGL initialization code for Spacetimewar.)

I have a few more things to mention about the games that consumed my life recently:

For me, one of the most "emergent" things about GTA4 was finding cars. The Turismo sports car is one of the best cars in the game (I compared it to the other sports cars, but not extensively, since I kept coming back to the Turismo). "Best" means "accelerates well", "handles well", and "doesn't explode". As the Turismo has its engine in the back, the game's damage model makes it almost invulnerable, which is nice. They're rare, and they're consumable (if driven into the water). So, this led to a minigame for me: find and collect every Turismo in sight. By the time I was done, I had parked two Turismos in front of each of my safe houses.

Playing D&D 4e is a remarkable turn of events for me. When I was at Caltech, I used to laugh at the people who played D&D in a common room. Of course, I had been exposed to the game system through Neverwinter Nights and Knights Of The Old Republic, but playing the tabletop game seemed strange to me. Later, I flirted with playing 3.5e with a few friends, but that petered out quickly. 3.5e's rules were stupidly complex (I can keep track of complicated rules - that's my day job, after all - but not stupid rules, like "full attacks"), and I hated the Vancian magic system ("fire and forget"; the less you know about it, the better). Recently, I heard about how 4e had simplified the rules and eliminated Vancian magic, which immediately hooked me. Indeed, 4e's rules make a lot more sense, and low-level wizards are actually fun to play now. In fact, I've gone so far as to DM a campaign for my best friend, who is controlling 6 PCs simultaneously. It's great fun, but also time-consuming; fortunately, it's not all-devouring like a video game would be.

Geometry Wars 2 fixes all of the things that I disliked about the first one (insanely rapid difficulty progression and insanely nasty penalties for death), and looks way better. Now I've got to make Spacetimewar look as good.

Go play Braid, if you haven't heard about it already. I like to call it, A Lesson Is Learned But The Damage Is Reversible.

8/14/2008 - I return!

Where did I go? To places called GTA4, D&D 4th Edition, Geometry Wars 2, and Braid. And work, although I have far fewer magical powers there. Now that I'm back, thanks to the finiteness of my addictions, I'm working on nuwen.net again, and will soon resume working on Spacetimewar.

I've been very busy this month. (Though not with the distro, which I built over the weekend. After 30 releases, building the 31st release isn't difficult, even when Boost's build system breaks my scripts yet again.) A bunch of stuff has been conspiring together to consume all of my time. At work, finishing VC9 TR1 and starting VC10 involved juggling more things than usual for a while. At home, Demerzel's crashing problem was driving me nuts and wasting my time.

The good news is that I've figured out what Demerzel's crashing problem was. The C0 stepping (i.e. initial version) of my QX9650 processor suffered from an FSB noise issue, leading to instability. Intel didn't recall the C0 stepping because this problem was said to manifest itself on cheap 4-layer motherboards only, not on the expensive 6-layer motherboards that Extreme processors would be used with. Experimentally, I conclude that this is a blatant lie, as I have an expensive 6-layer motherboard. (I have a Gigabyte GA-X38T-DQ6 Revision 1.0 motherboard; a Revision 1.1 with unspecified changes, and some layout differences, was released, but I don't know why.) After learning about this a couple of weeks ago, I decreased my FSB frequency from 1333 MHz (333 MHz quad-pumped) to 1066 MHz (266 MHz quad-pumped), also decreasing my CPU frequency from 3.0 GHz (333 MHz x9) to 2.4 GHz (266 MHz x9), and I have enjoyed rock-solid stability since then. As I'd like to avoid running Demerzel at 80% of its capacity, I'll be "upgrading" its processor to a non-Extreme Q9550 (2.83 GHz, which is 333 MHz x8.5). That's guaranteed to have a C1 stepping (i.e. fixed version), and I'll get 100% of the memory bandwidth and 94% of the CPU frequency that I originally wanted, which I can definitely live with. Fortunately, my motherboard supports the Q9550, so I won't have to upgrade motherboards (which still scares me). It requires a BIOS flash, but I've done that twice with this motherboard already.

There are two items of extremely good news. First, despite suffering dozens of spontaneous reboots and hangs, I don't appear to have suffered any hard drive corruption whatsoever. (My SAS RAID-1 array reports its status as Optimal, the computer boots and runs just fine, chkdsk is happy, and none of my files appear to be mangled.) This is despite the fact that at least two of the crashes happened during scheduled defrags. Except for the processor, the stability of my hardware and software stack - including and especially Vista - really impresses me. I was dreading that I'd lose everything and have to start over from Reason again. I even suffered a crash during Windows Update, but a System Restore fixed everything. Second, I figured out the workaround of decreasing the FSB frequency before Vista SP1 was released. Crashing during that installation would not have been good.

Even at 80% of its capacity, Demerzel is stunningly fast; I now do quad-core builds of Boost.

I finished reading Peter F. Hamilton's Night's Dawn "trilogy", which is actually a single massive SF novel broken up into three hardcovers or six paperbacks. Reading over 3000 pages of SF isn't instantaneous, even for me. (Yes, that's three thousand pages.) It was entertaining, but I certainly wouldn't rate it anywhere near Deepness or Fire.

I also read a new collection of short stories by Greg Egan. Unfortunately, it wasn't a collection of new short stories by Greg Egan; I had already read 4 of the 5 stories in the book. Still, Greg Egan is one of my "buy, sight unseen" authors, and the new story "Dark Integers" is a sequel to one of my favorites, "Luminous". "Dark Integers" also contains the only lines of C++ that I've ever seen in SF (short stories or novels, unless my memory is truly failing me). They're even accurate lines - it must have been terribly tempting to follow long int with dark int , but Egan used dark instead.

Continuing my reading spree (which has been going on ever since I learned how to read), I finished I Am A Strange Loop by Douglas Hofstadter. Unfortunately, the only key insight within was an interesting fact about the Fibonacci sequence. 1 (trivially), 8, and 144 are the only powers (squares, cubes, etc.) within the Fibonacci sequence. Ever. This was proven with machinery from the proof of Fermat's Last Theorem.

I've also been discovering new board games. (My vices are monotonically increasing.) I picked up Ingenious, a hex-pair tile-laying game by Reiner Knizia, who developed Tigris And Euphrates. Ingenious turned out to be really fun, with simple rules. (Rule complexity doesn't especially bother me, given that I deal with template argument deduction and overload resolution for a living, but it does make a game harder to introduce to others.) I was also amused to find that Ingenious shares Tigris And Euphrates' scoring mechanic of "weakest area matters"; that's a game design insight all by itself.

2/28/2008 - Version 3.6 of my MinGW Distro contains an uncompressed flex.exe . I UPX all of the executables in the distro to significantly reduce its size. A couple of people reported that their virus scanners, e.g. Kaspersky, were complaining about flex.exe . (Norton AntiVirus 2008 was perfectly happy.) This false positive was caused by UPX replacing the executable's contents with seemingly random compressed data, which just happened to match the malware signatures in a few virus scanners. Not compressing flex.exe in the first place makes these virus scanners happy, and they apparently have no problems with any of the other executables.

2/27/2008 - Yay, I can update nuwen.net again!

I've started using my new computer, Demerzel. It's taken a while, but I'm almost finished with setting everything up. Infuriatingly, I'm experiencing spontaneous reboots and hangs. They aren't happening very frequently (once every couple of days to a couple of times a day), but any instability drives me insane. At this point I think the problem is a motherboard bug, as the reboots and hangs aren't very predictable (Supreme Commander: Forged Alliance hasn't triggered them, although it sometimes crashes itself, while Sins Of A Solar Empire has triggered them several times), and they even happen while idling.

I've always avoided hardware upgrades, except for new video cards. But now, I think I'll try upgrading Demerzel's motherboard. Apparently, a repair install of Windows allows it to deal with a motherboard upgrade. I might wait for Nehalem to come out in late 2008 (although bleeding-edge hardware is what got me into this mess in the first place), or I might get desperate before then.

Some good news: A full build of Spacetimewar takes 35 seconds on Demerzel, while it took 127 seconds on Reason. Having figured out OpenGL shaders, I'm really itching to work on Spacetimewar again.

Also, I completely reworked how I generate nuwen.net. Previously, I used a complicated system of Server Side Includes to stamp out things like my auto-colored tables. While this was better than doing it manually, it was ugly and difficult to extend. Now, I generate everything with a custom C++ program. This allows me to invent arbitrary meanings (Turing-complete, yum) for arbitrary syntax (well, I limit myself to anything that can be described with regular expressions).

I also noticed that my oldest colorization on the Image Hacking page, Endgames Turning, was saved as an ugly 8-bit PNG. (I must have uploaded it shortly after creating it on 3/28/2002, and back then I really had to worry about keeping my pages small.) I found my master copy and replaced the image on the page with a 24-bit PNG. It's still ugly, but slightly less so.

1/12/2008 - I have improved the readability of the Links page; check it out.

1/7/2008 - I have updated the Links page, significantly reorganizing it as well as adding new links and fixing stale links. The webcomic links, along with their bulky previews, will return with my next site update. I'll create a new page for them, since they were getting in the way of everything else.

https://nuwen.net/news2008.html (updated 3/15/2010)Stephan T. Lavavej
Home: stl@nuwen.net
Work: stl@microsoft.com
This is my personal website. I work for Microsoft, but I don't speak for them.