While waiting for my Masters course to begin, I decided to program a little game to keep my programming skills sharp. Here I am a year later and it’s still nowhere near any sort of complete state. It WAS going to be a very simple RTS game, but when I saw someone had worked out part of the map file format for one of my favourite games, Bullfrog’s 1996 game Syndicate Wars, my plans changed, and since then I’ve been adding in various Syndicate features to my game engine. I’m not sure whether I’ll ever have the time for a full ‘unofficial remake’, but the engine is a handy little sandbox in which to try out my various ideas. Here I thought I’d explain a little bit about my work to date on it, if only to keep track of the things I have and haven’t done.
During my undergraduate degree, I taught myself the basics of OpenGL, mostly thanks to the infamous NeHe tutorials. Since then I’ve always favoured doing any graphics work in OpenGL, although for various reasons I chose to do my undergraduate final year project via Direct3D, with decidedly mixed results. For my game engine, I stuck with OpenGL, and extended my codebase to fully make use of more advanced features such as Vertex Buffer Objects and GLSL shaders. One of the neat things about the original Syndicate Wars was its use of light and shade. The map was dynamically lit by each light, as demonstrated by the darkness that ensued if you blew up every streetlight on the level. Each character and vehicle could cast a sprite shadow, which stretched and contracted according to the distance from the nearest lightsource. Not bad for 1996! Once I got ’swars’ maps loaded in, I knew I had to try and replicate these lighting features. As each map could have 100’s of lights in it, traditional methods of rendering would be rather limiting, so I decided to try and modify my OpenGL renderer to use some ‘deferred‘ features to accommodate them. This is a relatively new method of rendering which works primarily in image space, and essentially allows as many lights as your graphics card can draw, via ‘light volumes’. I render each light as a sphere mesh, and then perform per pixel lighting calculations on those pixels that end up inside this light volume. I wanted to try and take the original swars meshes and textures as far as possible, and I don’t think they look too bad when lit with per-pixel lighting!
Deferred rendering is neat in that as you save each rendering stage as a texture, you can look at those textures to ‘look inside’ its workings. Here’s some pictures to show this.
The first picture shows a zoomed out view of a swars map, complete with its lighting (and a glow-pass), while the second is the same view, but without lighting, and showing the light volumes (low poly isospheres made with blender). The third pic is of my ”deferred debug HUD’, which splits the screen up into four sections. Top Left is the calculated normals for the scene. Bottom Left is the basic texturing. Top right is the calculated lighting, and bottom right is my ‘material buffer’, a texture where I keep bits of information. For example, bits showing up in blue are in shadow.
I recently tried adding a ‘glow pass’ to the renderer, using a technique described by Tron 2.0 developers Monolith here. It looks OK, but I’ll need to tweak it a bit more when I get some spare time. Instead of using the alpha channel to keep which bits of a texture should be glowed like Monolith did, I used a combination of a ‘fullbright’ material, and pixels over a luminance threshold. This means that occasionally bits of a map getting a lot of bright light can glow a bit too much, as shown in the third picture. It does make all the windows and billboards look ‘lit up’ though.
Of Sprites and Men
In swars, only some of the games graphics were rendered using meshes; in 1996 there just wasn’t enough computing power! The various characters that inhabited the Syndicate world, and the various bits of street furniture in each level were drawn as sprites. To speed up rendering in my engine, I put all of the game’s sprites into a few large texture maps and save their texture coordinates into a ‘texture atlas’. I created code that will do all of the texture atlas generation, loading, and saving automatically, so my texture atlases are populated with sprites automatically, placed according to a kd-tree for each atlas image.
To render them, I use a variation on a process known as ‘billboarding’, which makes drawn sprites always face the screen, just like in the FPS classic, Doom. Always facing the camera is fine for games where you can’t look up or down, or for simple things like particles, but for a ‘freecam’ game such as my engine, pitching too far down would make the sprite look like it is falling over onto the floor, so I limit most sprites to only change their yaw to face the camera, a variation known as ‘Cylindrical billboarding’. This method still distorts somewhat as the camera pitch increases, but there’s no way around the flatness of sprites.
Hiding in the shadows
As previously mentioned, swars used a combination of vertex lighting and sprite shadows to add depth to the game. Part of my attempt to build a similar amount of realism into my engine is to allow direction lights to cast shadows onto the scene. This is done using a basic shadow map, which is automatically PCF filtered on my NVIDIA graphics card. Due to the naive implementation of shadow mapping I’m currently using, the only way I can get it to look acceptable is to use a large shadow map – currently 4096 by 4096! My deferred rendering system does mean that I can reuse this shadow map texture for multiple direction lights, by performing multiple fullscreen ‘shadow fill’ passes. Another neat shadowing trick I do is that every sprite in my engine can have another shadow, cast from the closest in-game light source. This is via ‘projecting’ the sprite into the scene along the direction the shadow would be cast, using simple cone shape geometry. This shape, combined with the zfail stencil technique often used in other shadowing methods, allows me to colour only fragments that would be hit by the shadow – they are then coloured by the alpha channel value of the source sprite, casting an outline of the sprite into the world.
Other neat tricks
For my engine, I wrote my own GUI library to power all the menus and in-game HUDs. I added the capability to render these GUIs into a texture, which can then be used in the game. I used this feature to put GUIs on the various in-game billboards. In the original game, these billboards ran adverts for upcoming Bullfrog games, the 2000AD comic, and the anime Ghost in the Shell. These were stored as low resolution ’smacker’ files, which I haven’t yet made a loader for. My GUI system allows me to make animated adverts all of my own, should I wish, so writing a smacker loader isn’t exactly high priority. Like a lot of the other GUI features I’ve implemented, the idea of rendering them to a texture has its basis in examining how Doom 3’s GUIs worked. I hope to eventually allow my projected GUIs to be interactive, if the camera, or the controlled character, is close enough.
The GUI System
One of the first things I programmed for myself back when I began teaching myself OpenGL was a simple GUI system that could be controlled via a text file. I’ve rewritten it a few times since then, but its overall design has served me well. I use it for all the menus in my swars engine, extending it with additional features as I try to mimick the original game’s GUI. I haven’t yet managed to recreate the ‘laser beam’ effect of the original game, but I’d really like to give it a go, time permitting. Each GUI is made up of a number of containers, each of which can contain other containers as children. Each container can be of a number of subtypes such as menus and textboxes, and each GUI is itself a container. I got this idea from working with the various GUI libraries in Java. GUIs can be controlled by the mouse, or by timed scripted events, which can changed a containers size, colour, text, and so on. Each GUI can also send an event back to the game engine, for example to tell it to load in the next level, or change the current weapon wielded by a player character.
The Sound System
I have written a very simple sound system for my engine, using OpenAL. It supports both ‘in world’ sounds and direct sound playback suitable for music tracks and GUI audio feedback. I’ve found OpenAL pretty easy to use, barring a few cross-platform issues that came up when I reused the sound engine for my university team project game. So far, it plays back as many sounds as OpenAL has channels available, sorted by their distance to the camera. When I get some free time I’m going to try and move that towards a priority system, where active sound sources are chosen for playback based on their importance: the sound of gunfire is probably more important than the sound of rainfall, for example.
It only supports basic .WAV files so far, as that is the format of all the sounds in Syndicate Wars. The swars sounds are all packed together into one big file, thankfully the .WAV format is clear enough to be able to detect where the individual sounds begin and end. I’d like to get .OGG support in at some point, as well as sound streaming, so I can more efficiently play music files.
Having spent so much time on the graphics, sound, and menus, I haven’t really got around to doing very much of the most important aspect of any game – the gameplay! The characters under the players control can be selected and told to move about the landscape, and civilians and vehicles can be dropped into the map, but there is no real interaction as of yet. I’ve implemented a really basic A* algorithm pathfinding system for the player characters, but it so far the heuristic only takes into account the difference in height across cells, and whether they are water. I haven’t yet got it removing cells that are underneath buildings, so a players characters will so far happily wander through buildings to get where you want them to go. Game AI fascinates me, and I really hope I find the time to mess around with adding some decent AI to the various inhabitants of swars, as the various weapons and characters would present lots of interesting challenges.
Although bits and pieces of the original file formats used by Bullfrog in Syndicate Wars have been decoded by various people, notably here and here, the task is by no means complete. I’ve spent quite a bit of time working on extracting useful information from the various files, including the game text and GUI information. By far the most time consuming task has been working out additional features of the map file format. Although enough of the file format has been decoded by other people to load in the basic vertices and texture coordinates, I’ve been able to extract information on sprites used in each map, and their locations. I also managed to figure out how vehicle navigation points go together, which allows vehicles in my engine to traverse the roadways present in each map. I’d never really even though about how a file could be reverse engineered before I started doing this, and I’ve found the whole process to be fascinating. It seems only fair that I share my findings, as after all I got such a jump start thanks to the work of others, so I’ll put up this information somewhere sooner or later. Maybe a wiki would be the easiest way.
Nothing just yet, I’m afraid. Eventually I’ll upload the latest ‘beta’ version, once I’ve figured out a method of using the basic Syndicate Wars CD folder hierarchy with my engine, my development folder is currently all in a bit of a mess. I obviously also can’t just distribute the original swars stuff as-is due to legal concerns :}