Everywhere I look, Matrices!

Warning:  technical brain-dump post.  I’ll try to have something more interesting to non-techies tomorrow.  :)

So there’s a major VectorStorm engine update that’ll be happening sometime in the near future;  it’s really needed for MMORPG Tycoon 2 (and any other 3D games I might feel like doing in the future).

Here’s the thing.  VectorStorm was written to be a 2D game engine, and so some of the things in it aren’t exactly what you’d want for a full 3D engine.  One example is its render management.  VectorStorm organises a game into a series of “layers”.  Each layer is drawn in front of the previous layer.  Now, in a game like MMORPG Tycoon 1 where your game was entirely 2D, and you wanted to make sure that your UI was being drawn in front of your game map, and tooltips were being drawn in front of the UI, and the cursor was being drawn in front of the tooltips, “layers” is a really sensible way to arrange things.  Within a layer, you couldn’t really predict how things would overlap.  So if it was important that object “A” draw in front of object “B”, you need to put object A in a higher layer than B.

However, in the world of 3D, we have depth buffers which handle this whole “what goes in front of what” thing for us more or less automatically.  Since MMORPG Tycoon 2 is a 3D game with a depth buffer, I mostly don’t need to draw things according to how they will overlap on screen;  the video card handles that for me.  Instead, I really want to draw according to whatever will allow the 3D card to render the game the most quickly.

In practice, this means that I want to try to bunch things together — if I’m going to draw grass, I want to draw all of my grass all at once.  If I’m going to draw clouds, I want to render them all in one go, instead of setting each one up individually and rendering them one at a time.  You get the idea.

What I’m setting up to do is called “material batching”;  it’s where everything using a particular material will be smashed together and rendered all at once.   In MS1, rendering the terrain worked like this:

  1. Set up ground material.
  2. Render 256×256 patch of ground.
  3. Set up grass material.
  4. Render 256×256 patch of grass on top of the ground.
  5. Move to the next patch and repeat.

For MS2, with material batching, I’m hoping that rendering the terrain will work like this:

  1. Set up ground material.
  2. Render all patches of ground.
  3. Set up grass material.
  4. Render all patches of grass.
  5. Done.

Since setting the materials is (generally) the most expensive step of rendering, making this change to set each material only once per frame should make VectorStorm run dramatically faster, especially on lower-end hardware.  But it’s not a simple change to make.

What’s more, some types of materials really need to be rendered in a special order.  In a 3D game, transparent objects usually need to be rendered last.  In VectorStorm in particular, any glowing objects need to be rendered last (This isn’t yet being done — this is why you can see glowing objects through other objects in Tycoon2-MS1).  By drawing materials in batches, it’ll mean that I can have objects which have glowing parts as well as non-glowing parts (such as selected buildings, which have glowing outlines and non-glowing polygons), and draw the non-glowing parts at the start of rendering, and the glowing parts at the end of rendering, whereas in MS1, I had to draw all of an object all at the same time.

Anyhow.  I don’t have this material batching system working yet, but it’s not far off.

The first step was moving matrix building from the OpenGL shader to within the VectorStorm code.  Matrices are a mathematical construct which relate how one thing is placed with respect to another.  For example, in MMORPG Tycoon, each world region has a matrix describing where that region is in the world.  Similarly, each building, PC, and monster have matrices describing where they are in relation to the region that they’re currently inside.  And, of course, the player himself is little more than a matrix that you can turn and move around with the keyboard and mouse controls.

It used to be that I used standard OpenGL calls to let OpenGL calculate all of these matrices for me automatically.  That worked great for the old hierarchical rendering code, but now that I’m getting ready to do material batching, I’ll need to know precisely where every object is, so I can render them outside of that hierarchical order.  Took me most of yesterday, but I finally got this all working properly.  Debugging broken matrix math isn’t a whole lot of fun!

(Amusingly, at work today I also spent most of the day debugging broken matrix math.  And then I came home and played a little Super Mario Galaxy 2, which is using more matrices than I’d like to think about.  I think I’m becoming hyper-sensitive to matrices.)

Anyhow.  Now that that’s done, the next step is to set up vsDisplayList to be able to transparently grow on demand.  In VectorStorm games, drawing commands are communicated from game code to the rendering engine via an object called a vsDisplayList.  Right now, you have to pre-declare how big you want your display list to be.  And then if you try to put more instructions in than you originally asked for, it generates an error.  And if you put fewer instructions in than you originally asked for, you’re wasting memory that could have been used for something else.  Allowing display lists to automatically grow and shrink as needed will make it much easier to use them, and should hopefully reduce overall memory usage as well.  Eventually, these growing-shrinking display lists will be used for building the material batches.  I’ve got this change about halfway completed now;  should be able to wrap it up tomorrow.

Once that’s done, all that’ll be left to do is the material batching itself, and defining some way to specify what order materials should be drawn.

Once that’s all done, the VectorStorm engine should (a) be faster, (b) be more flexible, (c) not have glows shining through solid objects, and (d) still be backwards-compatible with all earlier code.  It’s win/win/win!