Zombie simulator: optimisation & oh, the pain! PDF Print E-mail
Saturday, 02 January 2010 07:56

The pain

Yes, lets get the pain out the way first.

I had said I'd be using the Omari plugin for Blender to convert objects into Direct X format which could be imported into Irrlicht... well that caused me to spend quite a few hours chasing a phantom bug while trying to import a test object I"d created. It seems that Irrlicht's Direct X reader is glitchy, since importing anything more complex than a simple object caused the resulting mesh to have "artifacts", which showed up as vertices disappearing when I ran my optimiser over the mesh.

Of course, I thought at first it was the optimiser screwing the mesh up since the unoptimised mesh looked just fine... until I noticed that every time I compiled the program, the damage to the mesh would be different... even if I didn't change the code.

The main reason I went for this exporter originally was that Gandalf's b3d exporter could not be downloaded because his website had been eaten by the Balrog... (overrun by trojans and viruses, actually).

Luckily, I finally stumbled across a link on the Irrlicht forum to a copy of Gandalf's exporter, which I'm going to attach here, since it was released under the GPL and I don't want it to go missing again.

Many hours later, I finally was able to import and run the optimiser on a mesh without any visible damage to it.

The pain continued, when I discovered that the b3d exporter hasit's own issues to deal with:

  • 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 object's scale is tiny (Blender quirk)
  • There are far too many vertices in the imported mesh which slows down rendering (Blender quirk)
  • Vertices are specified in reverse winding order causing the back test culling to hide the polygons that should be visible (Blender quirk)
  • All objects vertices are defined in world coordinates, not relative to their own individual centers
  • The b3d format doesn't support object colours

The worst one to watch out for is the first issue, the negative texture coordinate on the Y axis. This shows up (under OpenGL at least) as the second mesh buffer that is textured having the texture coordinates screwed when the texture wrap is set to ETC_CLAMP. Not the first mesh buffer, nor anything after the second, just the second one. Weird.

The last issue, b3d doesn't support object colours, can be worked around in two ways from the Blender side of the equation. You can either texture the oject, or the quick way for simple objects, enable Vertex colours:

  • Select the object and hit F5 to go to the Material Buttons page.
  • Enable "V Col Paint" from the Matieral tab
  • Enter vertex paint mode on the object by pressing "V"
  • From the pain menu, choose "Copy from Material..."
  • Click OK on the window that pops up

This will assign the material used for the entire object to the individual vertices, which can then be imported into Irrlicht successfully using the b3d format.

Optimisation

Irrlicht is pretty limited when it comes to polygon culling. It's got the backface culling which is an old favourite, there's an oct tree implementation which is handy for dealing with large meshes, but apart from that, there's only a few different flavours of box clipping that apply to an entire object, basically comparing the area the camera can see with a box surrounding the object, and if they overlap, the object is drawn.

One of the properties of using an isometric camera, as I am in this project, is that it can't see the horizon, which means that there's no real need to use fancy 3D algorithms to hide objects. The camera's view is pretty much limited to a rectangle placed flat on the ground, which permitted me to get away with using a very simple 2D polygon culling algorithm.

This meant that I had to create my own rendering routine.

I achieved this by simply deriving a clone of  MeshSceneNode called OrthoMeshSceneNode, then making a few modifications to clip polygons before they are drawn:

  1. Calculate the rectangular ground area that the camera can see
  2. Examine each triangle in each mesh buffer to determine if the triangle overlaps the camera cube, ignoring the Y coordinate

Sounds simple... it's actually easier than that.

One of the more expensive parts (in terms of CPU use) of rendering a scene is calculating the transformation of every vertex in the scene, this means applying the translation, rotation and scaling of the object to all it's vertices to position it in the scene. Most of my scene is composed of fixed objects, buildings, terrain, roads, etc. I've already turned these into a couple of large meshes located at the origin (0, 0, 0). These meshes have no rotation or scaling applied, so the part of the process where these transformations are applied no longer needs to be done.

This saves a heap of CPU time, meaning that each triangle, ignoring the Y component, can be quickly and directly compared with the rectangle using just 2D math.

The end result is that a scene may contain tens of thousands of triangles, and yet maintain a frame rate in excess of 500 frames per second, quickly and easily culling 95%+ of the triangles from massive meshes, even on my mediocre hardware.

Camera tricks

You might have seen in screen shots from previous posts on this topic that I've been showing a zoomed out and close up view of the map. This is actually an easy trick to do using Irrlicht's orthographic camera matrix.

matrix4 matrix;
matrix.buildProjectionMatrixOrthoLH(zoom * screensize.Width, zoom * screensize.Height, near, far);
camera->setProjectionMatrix(matrix, true);

You can scale the area the camera sees by simply multiplying the width and height by a zoom factor. All I've done to achieve zooming is to alter the zoom factor using the mouse wheel, then update the camera by re-executing the above code.

blog comments powered by Disqus