3
\$\begingroup\$

First I'm going to give you my current situation and explain what I'm attempting. I will then pose my question below. I will attempt to include a "TL;DR" for those of you who prefer things to be concise.

OVERVIEW OF CURRENT SETUP:

The current representation of my game world is one where ' Pieces ' of a larger world are represented by three layers of IDs as follows.

//This is being done in C++
struct WorldPiece {
   ivec2 pieceLocation;         //Where ivec2 is a vector containing ints
   short backLayer[PIECE_SIZE]; //Where PIECE_SIZE is the size of the grid this piece contains, ie. for a 32x32 grid, this is 1024
   short middleLayer[PIECE_SIZE];
   short frontLayer[PIECE_SIZE];
};

The world as a whole is then composed of these pieces, which are loaded (and cached as needed) as the player traverses from piece to piece.

I currently have the data side of this all working. I may change the active data set and move around the camera (zoom in and out as well) and watch it all function.
I have also written a tool to support the loading and saving of pieces, and the painting of IDs across the world.

I HAVE THIS DATA:

Each ID within a piece maps to a texture that will be used when that ' cell ' of the piece must be rendered.

QUESTION:

How might I go about rendering each piece in such a way as to do this in as few passes as possible? I could render each cell at a time, but that would be slow. I could generate a list of vertices and indices and create massive VBOs, or sets of quads. Or perhaps there is a better way? I would prefer to keep the data separate from the rendering and to keep the size of the data as small as possible (so as to have a small as possible load time while the game is running). If possible, I would like this to be fast enough to render large swaths of tiles, (for example a 3x3 area of 1024 tiles apiece, or more). Since zooming in and out is possible it would be nice to provide this functionality to the player while still maintaining the detail of rendering the world.

If there would be a better setup for the world data to accommodate these requirements, I'm open to suggestions.

SIDE NOTES:

Please do not bring collision/physics, or other game-map-related things into this. I have that worked out and it will not be a bottleneck. I am merely concerned with a best approach to a rendering pipeline where speed is the top concern. Please do not suggest API X or ENGINE Y. I have looked around quite thoroughly and nothing available would properly provide what I need or integrate with my current codebase. I will be using a texture atlas.

tl;dr version- I have a grid based world that streams pieces in as needed, I would like suggestions on a fast way to render these pieces and nothing I have found anywhere has a suitable solution for my current situation.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

For a 2D grid game, you can keep things pretty low key and still get very good performance.

There are a few ways you can go about things.

1) Create a VBO and index buffer for each world piece. When you draw, draw all visible world pieces. Finding which ones are in your view rectangle should be easy, and if you have a lot loaded at once, using a quad-tree to organize the world pieces will help.

2) Create a single streaming VBO, and fill it with cells each frame. Find the world pieces in the view frustum, then clip their cells to the visible region, and copy the translated position and texture coordinates to the VBO and index buffer for rendering. This will probably be slower than option 1, but gives you some added flexibility.

3) Same as 2, but use different VBOs and index buffers for different textures and shaders. You should minimize the number of each (ideally one shader and one texture), but if you need to use different materials to pull off the look you're going for, this is the way to go.

4) Do the same as option 2 or 3, but use GPU instancing. This will let you get a bit closer to the efficiency of option 1 while retaining the added flexibility.

Of the four, option 1 is both the simplest and (in general) most efficient.

Also keep in mind that the size of your WorldPiece grids can have a big effect. You don't want them to be overly large. In a game I'm working on right now we have a similar setup to you. We found that using chunks that are approximately 1/2 the size of the visible screen worked fairly well. (32x32 tiles, in our case.)

When rendering, atlas your textures. As I mentioned above, you want as few draw calls as possible. That means as few state changes. A single WorldPiece should ideally require only a single texture that has all the tile data on it, and a single shader. Even if you strictly need more than one texture or shader, you want to keep the number small. If you have more than one, you want to use batch all the individual tiles that use the same texture and shader into a single draw call, so use option 2, 3, or 4.

\$\endgroup\$
7
  • \$\begingroup\$ Since I have the tool I wrote, what it does is, on save, generates an atlas from each texture used in the game. Then the game just needs that atlas at runtime. If needed I might have each piece on its own atlas, but I think one per world would be sufficient. Right now I'm slated to have pieces be 32x32, but since I can zoom in and out at will, the amount of screen real estate is arbitrary. I'm learning toward something like option 1 right now and culling WorldPieces is easy since I maintain a small subset of pieces that are currently nearby/visible. So would you say option 1 would be the best? \$\endgroup\$
    – Kerr
    Apr 16, 2012 at 23:31
  • \$\begingroup\$ So, for option 1, should I fill the VBO with several small squares and one for their indices? Also, how would this work with layers? One VBO per layer and one draw call each? \$\endgroup\$
    – Kerr
    Apr 16, 2012 at 23:37
  • \$\begingroup\$ You can just full the VBO with the vertices for all the non-empty tiles, for all layers, so that you literally just have a single draw call for the whole world piece. You can rely on index ordering or z-buffering to ensure the layers are drawn correctly. \$\endgroup\$ Apr 16, 2012 at 23:51
  • \$\begingroup\$ But since all I have is the root position of the WorldPiece should I just build a template VBO that is just a grid and send that it transformed by the root position? The tiles themselves have no locational information currently. \$\endgroup\$
    – Kerr
    Apr 16, 2012 at 23:57
  • \$\begingroup\$ Going to accept this for now, but an answer (to my comment) would still be very appreciated (: \$\endgroup\$
    – Kerr
    Apr 17, 2012 at 0:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .