Tuesday, March 13, 2007
What's the most annoying thing about trying to write a game engine? Ohh, I'm spoiled for choice, ok, I think the most annoying thing is getting together some half decent looking art so you don't go insane testing your engine with stick figures. Well, I recently came up with an interesting solution.. why not use some existing art from one of your most favourite games?
You may note, I've done this before. This time, I picked a game that is a little more recent, but not too recent, GTA3. No, not Vice City, not San Andreas, but those two are the same engine, just updated some.
As I've mentioned before, I have a liking of the OGRE graphics engine. So the first order of business was to get the models out of GTA3 and into OGRE's mesh format. The format used by GTA3 is the Renderman DFF file format. The individual dff files are concatenated together in the models\gta3.img file, which is indexed by the models\gta3.dir file. There's plenty of tools around to extract the GTA3 img file, so I didn't bother writing one.
I wrote a tool called dff2ogre (source) to convert a single model into a mesh.xml and optionally a skeleton.xml file. Using the OGRE command line tools, you can then convert the xml files into their binary forms. I put the resulting mesh/skeleton files into media\models in my OGRE directory and modified one of the sample programs to display it.
The first thing I noticed was that there were no textures.. this was to be expected as I hadn't converted any of them. The second thing I noticed was that the Y and Z axises were swapped. I should have went and fixed my converter to take care of this, but instead I just did a rotation about the X axis and forgot about it. This made a lot of my later code confusing and I wish I had bothered to correct the problem earlier. May this be a warning to you.
So, how about those textures? GTA3 stores textures in TXD files. Again, there are plenty of tools around to extract the textures from a TXD. I chose to extract them as TGA files and put them in the media\materials\textures directory of my OGRE directory. You also need to make material scripts with an entry for each texture, and some of the textures need scene_blend alpha_blend / depth_write off set in them (glass, trees, etc). I put my scripts in the media\materials\scripts directory and fired up my sample program again and now had lovely textures.
The next big problem was taking all these individual models and turning them into a city. Turns out there are files with the IPL extension in data\maps which list each model and its position and rotation in the world. This actually took some fiddling to get right. Again, the co-ordinate system is different, so you need to change the rotation, swap the Y and Z axis and negate one of them. The result was a city I could ghost around in. I only did the first island.
At this point, allow me to tell you how fantastic OGRE's scene manager is. I loaded up over 700 unique meshes, over 200,000 triangles, and ghosting around was smooth as if I only had 200 triangles loaded. The original game didn't do it this way, it would unload and reload meshes whenever you crossed arbitary zone boundaries. This is why there is so much effort to provide the textures in seperate TXD files and associate those files with the meshes, so the textures can be unloaded when they are not needed.
Anyway, the next little challenge was converting the player model to OGRE format. This was the first DFF with a skeleton that I had tried to convert and it took a little effort to get going. I modified dff2ogre to look for the PED.IFP file, which contains all the character animations in the game, and converted them to OGRE animations. This, of course, didn't work first time and took a lot of fiddling to get going well. Again, those swapped axises are a bitch.
I wrote some really simple ray casting code to keep the player character above ground. Basically, you just start at a point in the middle of the mesh and cast a ray straight down. OGRE gives you back a sorted list of the entities in the world that you have hit. Ignoring the player entity itself, the first entity you hit is likely the ground.. but you really need to check every polygon in every entity that OGRE claims you have hit to ensure that you find the polygon that is closest to your starting position. Once I had the intersection point with that polygon I modified the position of the player node so the feet of the player mesh were above that point.
Similarly, you can cast a ray from the middle of the player mesh in the current direction and prevent the movement in that direction if it will result in the mesh intersecting some part of the world. Play the right animation and you now have a player character that can run around the world.
Thing is, running around the world was only a small part of GTA3. A much bigger part.. one could even say, the reason you played the game.. was vehicles. So I picked a random vehicle model (the kuruma if you're interested) and converted it to OGRE format. Like the player character, vehicle models have skeletons. Some parts of the vehicle are low poly or damaged parts, so I hid the submeshes that were attached to those bones. The models look good.
So how do we make this car go? At first, I tried writing similar ray casting code to what I had done with the player character. The results were unrealistic, to say the least, and I knew it would be a lot more effort to get anything reasonable out of it. As a first try it was ok, but something was lacking.
Physics. OGRE has tight bindings to the Open Dynamics Engine, so I figured this would be my best bet. Unfortunately, the OGRE bindings do not include the "new" trimesh collision object, so making the vehicle dynamics object interact with the world was still up to me in the form of ray casting. The result was less than impressive. I tried both simulating the vehicle as a box which hovers over the terrain and as a box with four wheels connected with a suspension. Neither resulted in good results.
So, after feeling lost for a while, I decided to try the Bullet Physics Library. I expected it to be difficult to use, but was pleasantly surprised. The trimesh collision objects work very well and are impressively fast (again, over 700 models, 200,000 polygons and excellent performance). My only wish is that I could use a trimesh for the vehicle model.. but for some reason you can't have weight assigned to trimesh collision objects (they can't move). Most importantly, Bullet has a vehicle object, so getting some basic car dynamics going is easy.
Unfortunately, even with extensive tweaking and fiddling, I was unable to get my vehicles to not flip over so much and, more annoyingly, not get embedded into the ground or drive through walls. Frustration abounds! I spent way too much time worrying about this and decided to just move on. Maybe there will be improvements to Bullet's vehicle class in the future.
The other major facet of GTA3 was the weapons. Like the wheels on the vehicles, GTA3 puts all the meshes for weapons in a single file, creatively called weapons.dff. I actually manually extracted these from the mesh.xml file, it was easier than writing a tool to do it. After screwing around with trying to place the weapon into the player's hand by following bones and applying rotations and translations manually, I actually bothered to read the docs and discovered that OGRE provides a function called attachEntityToBone() which does it all for me. Fun eh?
After parsing data\weapons.dat I had all the information I needed to animate the player character and do "instantaneous" firing. Of course, I had nothing to shoot at, so I got myself another pedestrian model (a mafia dude) and wrote code to animate him to receive damage and fall down when he died. The result was almost convincing.
What was and still is, lacking is the particle physics which make the game look so pretty. There is a nice big data file called particle.cfg which outlines how to make these particle effects, but it contains LOTS of parameters and I'm not sure what half of them mean. Of course, it seems that their particle systems are really only one particle.. in OGRE I would just use a billboard. I ended up doing this for the muzzle flash of the weapons, and it looks reasonable, but its not very exciting.
And so, I guess we're up to the point in the story where I explain what I have learned. Writing a game engine is a lot more fun when you have art to show and a knowledge that the art is in a form that can work. Unfortunately, writing a game engine is still a lot of work. I hope one day I'll finish this project. Maybe someone will put together a GTA3 mod with all free art and my engine could be used to make an all free game. Maybe someone would like to work on making this project playable. Maybe someone would like to make a multiplayer game using it as a base. If so, let me know and I'll release the code.. but at the moment it is largely useless.