|
|
|
|
|
The Making of Sprite-o-Mat |
|
|
Ideas
The project that ultimately ended up being known as "Sprite-o-Mat" started in late january of 2007. With my previous releases being several years in the past ("Catch a cold" for Break- point 2003 and "Demotron++" together with Creative Minds for Breakpoint 2004) and the upcoming Breakpoint 2007 being my 10th Mekka-Symposium / Breakpoint in a row, I decided that it was about time to get something cooking for the bigscreen once more. When I started I really didn't have much of a concept. In fact the only think I knew I wanted to have was a greetings part and something to do with post-processing. Not what one could call a well laid-out plan by any measure.
The code begins
The first thing I did was to write a small pixel shader for the brightness and blurring-passes (also known as "blooming"). To test the shader I created a plane consisting of 64x64 cubes with random colors for each frame. It reminded me of something out of 'fr-025: the popular demo', so even in this early stage it looked pretty cool.
I then added routines for rendering text, and voila: my initial requirements had been met. This alone wasn't really much material for a 4k, so I had to figure out something more interesting to add to the intro. After a few inspirationless days I started messing around with my test-code and I ended up extending the 2D grid to a 3D grid (64-cubed in size), implemented a marching cubes function and instead of constructing triangles in the grid (which usually requires quite a large lookup table) I just set the cubes at the gridpoints when they were in a certain intensity range. It turned out looking rather ugly when the metaballs were moved, since they were either visible or not (hello popping!).
I therefore started stacling the cubes depending on their gridpoint intensity throughout the whole influence range of the metaball. This looked rather interresting, but unfortunately lead to quite a bit of performance loss (because of the amount of cubes and the draw calls for each of the cubes), so I decided to give the good ol' point sprites a try. They did not disappoint.
More effects
With the rendering base done I tried various effects and finally implemented a tentacle-like object which I had wanted to include in my 2003 4k intro but somehow fucked it up due to some Windows 2000/XP ordinal import issues and general assembler mischief. Since I already liked the metaballs moving and scaling the sprites I hoped that this rendering technique would also work for the tentacle object. Luckily for me, it did. I still felt like there was something missing, and a few days later I built a cube at the grid borders which had the visual effect of "imprisioning" the tentacle, along with some code to make the borders change color when the tentacles touched it.
Sprite-o-Mat (image courtesy pouet.net)
The next effect I worked on was a flight through a metaball field, but I quickly discovered that it required a significant number of metaballs to look even remotely decent (around 256 metaballs in the 64-cubed grid actually looked rather good), and since the amount of metaballs was a known bottleneck, the performance was horrible. I had already done some optimizing tricks where I used a finite influence range so that I wouldn't have to add intensities to every gridpoint for each metaball, but it wasn't enough. Then something struck me - instead of recalculating grid intensities for all metaballs every time they were moved one plane forward I only added the intensities for newly inserted ones. That way, the complete volume was shifted in the Z direction by copying plane n to plane n+1. At first I simply used the memcpy command, but I ended up manually copying each plane and then multiplying each intensity value by 0.98 to let the field slowly dissolve. After that, the text effects were easy. I simply rendered the text to a 64x64 texture and copied the pixel values to any of my Z planes inside the volume.
Music and crunching
Now I had two nice effects plus my initial greeting part done, but still no music. The filesize was already above 3 KB (fully compressed) so I knew I had to start thinking about the sound. It had become a common practice for 4k intros to (ab)use the samples stored in the gm.dls, and I decided to have a look at the samples to see if there was some way to utilize them without having the music sound like General MIDI. After some few days I had written a cheesy tune and a small player for it. It sounded really cheap, like something you would expect from General MIDI sounds. At this point I thought about implementing some sort of softsynth, but I quickly discarded the idea because there was simply not enough space for it. I ended up modifying the GM sounds by layering a synth basedrum over the bassdrum sample and also by creating a quick and dirty FX chain with a delay line and a lowpass filter. After that was done the tune sounded okay. Not great but not crap either.
This was as far as I got before leaving for Breakpoint 2007 and I felt quite happy about it. The only thing I had left to do was a quick scene layout and to sync the intro to the music, as well as doing the final compression and code optimizing to get the whole thing down to 4096 bytes of course. As it turned out (as these things often do), most of the remaining time was spent bringing the filesize down to below the limit.
After syncing the music and polishing the scenes to a point I found acceptable the intro was around 4300 bytes so I had to find a way to get rid of the 200 byte overweight. This was the first time I had used Crinkler for packing, and I really liked it. The concept of a compressing linker really appealed to me, and I especially liked the different packing algorithms for the data and code sections as well as the section reordering. It didn't hurt that it was nicely integrated into Visual Studio either.
Playing around with my code and the reordering instructions I soon learned that no matter what I did, it didn't result in a smaller binary. Even when I converted the metaball calculation routine to pure assembler I was disappointed to see that the final binary just got bigger, even though the uncompressed binary got considerably smaller. I'm not really into packaging techniques, but from what I could gather, the final compressed binary got bigger because the style of my hand-written code was so different from what the compiler prodced and this lead the packer to be confused and produce larger binaries. This was the most irritating part of getting the filesize down -- I thought I did something small and clever, but the packer didn't. From there on in, it was basic "trial and error" until the intro got below the 4096 byte limit. What ended up saving me was shaving those 200-or-so bytes by shifting as much stuff as I could from the code section to the data section, since the data compressed way better than the code.
Compatibility issues
At close to two in the morning (eight hours before the deadline) I had finally managed to get the compressed binary below 4096 bytes and I was feeling pretty good. This was before I remembered that the compo machine had an ATI graphics card, and I had been using a nVidia card the entire time I had been working on the intro. Fortunately for me, the guy sitting next to me had an ATI graphics card in his computer and I got to try the intro on it.
As you might have guessed already, it didn't work. The first and fifth scene were just black screens. Oh joy! Eight hours for me to find the bug and fix it before the deadline.
It took me quite a while to find the bug, and while looking for it I also discovered some strange differences in the way point sprites work on nVidia and ATI. It seemed that on ATI cards, point sprites only got scaled to a certain extent (64 pixels IIRC) while on nVidia they can basicly cover the entire screen. Luckily for me, the effect wasn't really noticable during the intro (even when doing direct comparisons) apart from the black screens of course. It seemed to be related to negative sizes on the sprites, which was returned from one of my functions during those scenes. ATI's behaviour was in practice correct (a negative sprite shouldn't really be valid), and nVidia just ignored the error and took the absolute value of the sprite size instead. Pretty stupid behaviour by nVidia.
At six o'clock in the morning (two hours before the deadline) I had my intro running correctly on both ATI and nVidia and I submitted it to the compo system before getting under the table and catching a few hours of sleep. As the story goes; "the rest is history" -- 'sprite-o-mat' took fist place in the 4k intro competition.
Final words
I hope you have enjoyed this short "making-of" article, as well as my thoughts and experiences about it. "So what is next?" you might ask. Well, because I do not want to hear another cheesy GM.DLS sound for as long as I live (and to give Gargaj a run for his money, I have started developing a completely new softsynth for use in my upcoming projects. The softsynth is nearing completion, and my next release will (hopefully) be for Evoke. As for the concept for this intro, I'm looking into expanding the concept from 'sprite-o-mat' (I really liked the volumetric aspects of it), but who knows what it will end up looking like. Inspiration comes and goes and what the final product will be is impossible to predict.
If you are over average interested in 4k intro making, you can check out the source code for 'sprite-o-mat' which is included with this issue of ZINE as a small bonus. I'd like to hear your feedback, and you can send questions and whatnot to gopher at hazard-designs.de
Go back to articlelist |
|
|
|