Zombie simulator: buildings E-mail
Tuesday, 29 December 2009 07:59

Well, now that the roads are in, it's time to add some buildings.

Taking my usual organic approach to development, this means that first of all I'm going to construct the walls, then worry about details such as doors, windows, different textures etc.

Identifying buildings

Buildings are defined by blocks of coloured pixels on the map. Rather than simply construct one huge building for every coloured block, I'm going to divide up large areas of colour into a series of smaller buildings.

The process is quite simple: scan across each row of the map looking for one of the building colours. When one is found, scan down and across the map from the starting point to find the largest horizontal and vertical rectangle that's the same colour, then, if the block is large enough to hold a building, simply store a building definition in an array and clear out the corresponding pixels on the map covered by the area of the building. Keep scanning the rest of the map.

A separate list is constructed for each of the different colours that identify buildings.

That's the easy part. The next bit is actually constructing the building.

Constructing buildings

There's not many cities that have only single floor buildings, so I'm going to need to handle multiple floors. Additionally, I want the walls of the buildings to be destructible, so I'll need a list of walls that each buliding is constructed from.

To handle all this, I've created a series of classes to store the information.

  • cBuildings
    Contains a separate list of cBuilding objects for each of the building types.
  • cBuilding
    Defines the building, including the area on the map which it occupies and contains a list of cFloor objects, one for each level of the building.
  • cFloor
    Defines the floor. Building floors can be smaller than the overall area that the building takes up. This class also contains a list of the CWall objects that define the boundaries of floor.
  • cWall
    Defines a wall piece, each of which corresponds to a single pixel location on the map, which means that each wall piece could be up to 256 world units in size. This will also contain other properties, such as the level of damage that the wall has suffered, the type of brick texture to use and so on.

From the Irrlicht perspective, I've decided (for now) to create one Mesh per building, with each floor defined as a separate Mesh Buffer, which will (hopefully) permit individual floors to be rebuilt on the fly as the walls get damaged by the surrounding battles with the zombie hordes.

Modeling the walls

I initially toyed with the idea of manually generating the wall models from with C++, but it quickly became obvious that it would be necessary to use a 3D modeling tool to create the models then load them at run time.

Ignoring doors/windows/etc., I had to build six wall pieces, one for each of the corners and one for both the horizontal and vertical walls, so I broke out Blender and got to work. It turns out that 1 unit in Blender equals one unit in the Irrlicht environment, which means that in order to create a wall that's 256 units in size, the model had to be absolutely massive in Blender (the default cube in Blender is 1 unit in size)! To get around this, I'm working at 1/10th scale in Blender, then scaling the object up to full size in Irrlicht.

 

The image above is a screen shot taken in Blender showing the six separate wall models after they've been textured. Since Irrlicht can't read Blender files, I had to export them in .X (Direct X) format. After my first attempt at reading them into Irrlicht, I found a few issues in the way the exported data is arranged.

  • The first MeshBuffer in the imported mesh is always blank
  • The Y component of the texture coordinates is always negative, which screws up texturing if the texture is clamped to the size of the mesh
  • The scaling issue previously mentioned
  • Vertices are specified in reverse winding order causing the back test culling to hide the polygons that should be visible
  • There are far too many vertices in the imported mesh which slows down rendering

To get around this, I wrote a function that basically created a new mesh from the imported one and fixes all of these problems, in particular the last one with excessive vertices. The reson why there are so many extras must be the way that Blender internally stores the data. Each triangle has a unique list of vertices, which means, for example, a square plane, composed of two triangles with only four corners actually has 6 vertices, two of which are duplicates. In some situations you do want duplicates as it will allow you to have unique texture coordinates for two polygons that share common vertices, but most of the time it's just a waste of resources.

One other little optimisation I performed was to delete the back faces from the wall models. Because the camera's location is fixed facing one direction, there's no need for the hidden walls to be included in the mesh. I've attached a rear-view of the walls to show you what I've deleted, which has about halved the number of triangles needed to render the walls.

 

A work in progress

Here's a couple of shots from the zombie world with the walls up. It's still very much a work in progress, so all I've done is colour code the wall pieces to show what the type of building it is. The end product will have a larger variety of wall textures and designs, but for now this will be sufficient to allow me to move on.

Because I've rendered the walls as separate pieces, the amount of data points required to generate the entire map is pretty large, resulting in about 15,000 triangles for the entire map above. This runs fine on my mediocre box, but it really needs to be better, so for my next installment, I'll go into some specific optimisations that I've done that have drastically improved the frame rate.

blog comments powered by Disqus