Thursday, August 30, 2012

Chunk based Vertex management

For my project "Heightmap Terrain Editor" I had to deal with very large maps (>2048x2048). To display theese maps I've used chunk based Vertex management. Basically, each chunk is 64x64 faces sized and is stored separately in an array. My method has also very high memory consumption, but for all normal usages it should work fine. First, we initialize an array to keep chunks and vertices:
private volatile VertexPositionColor[][] _vertexChunks;
private int[][] _indicesChunks;

What we do, is simply instance a two dimension array. The first dimension keeps the chunk index and the second dimension keeps the index of the element in the chunk. Now we have to initialize those vertices to start displaying them. For initializing I used two functions, one for vertices and one for indices.
        private void SetUpVertices()
        {
            _vertexChunks = 
                  new VertexPositionColor[_terrainWidth * _terrainHeight][];
            for (int cx = 0; cx < _terrainWidth; cx++)
            {
                for (int cy = 0; cy < _terrainHeight; cy++)
                {
                    _vertexChunks[cx + cy * _terrainWidth] = 
                          new VertexPositionColor[65 * 65];
                    //Chunk mode
                    for (int x = 0; x < 65; x++)
                    {
                        for (int y = 0; y < 65; y++)
                        {
                            _vertexChunks[cx + cy * _terrainWidth]
                                         [x + y * 65].Position =
                                           new Vector3(cx * 64 + x, 
                                                       0,
                                                       -(cy * 64 + y));

                            _vertexChunks[cx + cy * _terrainWidth]
                                         [x + y * 65].Color = Color.White;
                        }
                    }
                }
            }
        }

The greatest problem is to deal with vertex and chunk addressing.With this snippet we initialize 64x64 faces for the chunk based on chunk position and vertex position. The indices aren't a big thing using the snippet above and change it according to indices initialization.
        private void SetUpIndices()
        {
            _indicesChunks = new int[_terrainWidth * _terrainHeight][];

            for (int cx = 0; cx < _terrainWidth; cx++)
            {
                for (int cy = 0; cy < _terrainHeight; cy++)
                {
                    //Chunk mode
                    _indicesChunks[cx + cy * _terrainWidth] = 
                                           new int[(64) * (64) * 6];
                    uint counter = 0;

                    for (int y = 0; y < 64; y++)
                    {
                        for (int x = 0; x < 64; x++)
                        {
                            int topLeft = x + y * 65;
                            int topRight = (x + 1) + y * 65;
                            int lowerLeft = x + (y + 1) * 65;
                            int lowerRight = (x + 1) + (y + 1) * 65;

                            _indicesChunks[cx + cy * _terrainWidth]
                                            [counter++] = lowerLeft;

                            _indicesChunks[cx + cy * _terrainWidth]
                                            [counter++] = topRight;

                            _indicesChunks[cx + cy * _terrainWidth]
                                            [counter++] = topLeft;

                            _indicesChunks[cx + cy * _terrainWidth]
                                            [counter++] = lowerLeft;

                            _indicesChunks[cx + cy * _terrainWidth]
                                            [counter++] = lowerRight;

                            _indicesChunks[cx + cy * _terrainWidth]
                                            [counter++] = topRight;
                        }
                    }
                }
            }
        }

To draw the vertices, we have to get the chunk the viewer is currently standing on
        private int GetChunk(int x, int y)
        {
            int chunkX = x / 64;
            int chunkY = -y / 64;
            if ((chunkX + chunkY * _terrainWidth) < _vertexChunks.Length)
                return (chunkX + chunkY * _terrainWidth);

            if (chunkX > _terrainWidth - 1)
                chunkX = _terrainWidth - 1;
            if (chunkY > _terrainHeight - 1)
                chunkY = _terrainHeight - 1;

            return (chunkX + chunkY * _terrainWidth);
        }

The snippet above is just for calculating the chunk index of the chunk placed at the viewers position. We know that each chunk has 64x64 faces (lines 3&4) and can divide the position trough the chunk size. At next we only have to check whether the chunk exists. For displaying we have to add some chunks around, but's that an easy job.

At next, we draw the drawable chunks.

                    for (int i = 0; i < _drawnchunks.Count; i++)
                    {
                        device.DrawUserIndexedPrimitives(PrimitiveType.TriangleList,
                                                         _vertexChunks[_drawnchunks[i]],
                                                         0,
                                                         _vertexChunks[_drawnchunks[i]].Length,
                                                         _indicesChunks[_drawnchunks[i]],
                                                         0,
                                                         _indicesChunks[_drawnchunks[i]].Length / 3,
                                                         VertexPositionColor.VertexDeclaration);
                    }

Thats it for now. Next time we look a little bit on triangle picking and other editor related stuff.

No comments:

Post a Comment