0
votes

im wondering (im pretty sure there is) if there is a better way to handle the data im filling into my vertex / index buffer. At the moment my struct for the vertices in the terrain looks like this:

struct VertexType
    {
        D3DXVECTOR3 position;
        D3DXVECTOR2 texture;
        D3DXVECTOR3 normal;
    };

I am randomly generating the terrain everytime the game starts up and based on this heightmap i am calculating the normals and the texture coordinates. At the end i am filling a vertexarray in this way:

// Calculate the number of vertices in the terrain mesh.
m_vertexCount = (m_terrainWidth - 1) * (m_terrainHeight - 1) * 6;

// Create the vertex array.
m_vertices.resize(m_vertexCount);

// Initialize the index to the vertex buffer.
index = 0;

// Load the vertex and index array with the terrain data.
for(j=0; j<(m_terrainHeight-1); j++)
{
    for(i=0; i<(m_terrainWidth-1); i++)
    {
        index1 = (m_terrainHeight * j) + i;          // Bottom left.
        index2 = (m_terrainHeight * j) + (i+1);      // Bottom right.
        index3 = (m_terrainHeight * (j+1)) + i;      // Upper left.
        index4 = (m_terrainHeight * (j+1)) + (i+1);  // Upper right.

        // Upper left.
        tv = m_heightMap[index3].tv;

        // Modify the texture coordinates to cover the top edge.
        if(tv == 1.0f) { tv = 0.0f; }

        m_vertices[index].position = D3DXVECTOR3(m_heightMap[index3].x, m_heightMap[index3].y, m_heightMap[index3].z);
        m_vertices[index].texture = D3DXVECTOR2(m_heightMap[index3].tu, tv);
        m_vertices[index].normal = D3DXVECTOR3(m_heightMap[index3].nx, m_heightMap[index3].ny, m_heightMap[index3].nz);
        index++;

        // Upper right.
        tu = m_heightMap[index4].tu;
        tv = m_heightMap[index4].tv;

        // Modify the texture coordinates to cover the top and right edge.
        if(tu == 0.0f) { tu = 1.0f; }
        if(tv == 1.0f) { tv = 0.0f; }

        m_vertices[index].position = D3DXVECTOR3(m_heightMap[index4].x, m_heightMap[index4].y, m_heightMap[index4].z);
        m_vertices[index].texture = D3DXVECTOR2(tu, tv);
        m_vertices[index].normal = D3DXVECTOR3(m_heightMap[index4].nx, m_heightMap[index4].ny, m_heightMap[index4].nz);
        index++;

        // Bottom left.
        m_vertices[index].position = D3DXVECTOR3(m_heightMap[index1].x, m_heightMap[index1].y, m_heightMap[index1].z);
        m_vertices[index].texture = D3DXVECTOR2(m_heightMap[index1].tu, m_heightMap[index1].tv);
        m_vertices[index].normal = D3DXVECTOR3(m_heightMap[index1].nx, m_heightMap[index1].ny, m_heightMap[index1].nz);
        index++;

        // Bottom left.
        m_vertices[index].position = D3DXVECTOR3(m_heightMap[index1].x, m_heightMap[index1].y, m_heightMap[index1].z);
        m_vertices[index].texture = D3DXVECTOR2(m_heightMap[index1].tu, m_heightMap[index1].tv);
        m_vertices[index].normal = D3DXVECTOR3(m_heightMap[index1].nx, m_heightMap[index1].ny, m_heightMap[index1].nz);
        index++;

        // Upper right.
        tu = m_heightMap[index4].tu;
        tv = m_heightMap[index4].tv;

        // Modify the texture coordinates to cover the top and right edge.
        if(tu == 0.0f) { tu = 1.0f; }
        if(tv == 1.0f) { tv = 0.0f; }

        m_vertices[index].position = D3DXVECTOR3(m_heightMap[index4].x, m_heightMap[index4].y, m_heightMap[index4].z);
        m_vertices[index].texture = D3DXVECTOR2(tu, tv);
        m_vertices[index].normal = D3DXVECTOR3(m_heightMap[index4].nx, m_heightMap[index4].ny, m_heightMap[index4].nz);
        index++;

        // Bottom right.
        tu = m_heightMap[index2].tu;

        // Modify the texture coordinates to cover the right edge.
        if(tu == 0.0f) { tu = 1.0f; }

        m_vertices[index].position = D3DXVECTOR3(m_heightMap[index2].x, m_heightMap[index2].y, m_heightMap[index2].z);
        m_vertices[index].texture = D3DXVECTOR2(tu, m_heightMap[index2].tv);
        m_vertices[index].normal = D3DXVECTOR3(m_heightMap[index2].nx, m_heightMap[index2].ny, m_heightMap[index2].nz);
        index++;
    }
}
// shrink heightmap because its no longer in use...
m_heightMap.clear();
m_heightMap.swap(m_heightMap);

The code for this is partly taken from rastertek.com. I am wondering if this isn't a lot of redundant data being saved (6 vertices per quad). The problem though is, that i kinda need this structure later on to form the buffers for my quadtree. Here is the code for filling the buffer at each node (every node has its own buffer - is there a better way to do this?):

node->triangleCount = numTriangles;

// Calculate the number of vertices.
vertexCount = numTriangles * 3;

// Create the vertex array.
vertices = new VertexType[vertexCount];

// Create the index array.
indices = new unsigned long[vertexCount];

// Create the vertex array.
node->vertexArray = new VectorType[vertexCount];

// Initialize the index for this new vertex and index array.
index = 0;

// Go through all the triangles in the vertex list.
for(i=0; i<m_triangleCount; i++)
{
    // If the triangle is inside this node then add it to the vertex array.
    result = IsTriangleContained(i, positionX, positionZ, width);
    if(result == true)
    {
        // Calculate the index into the terrain vertex list.
        vertexIndex = i * 3;
        // Get the three vertices of this triangle from the vertex list.
        vertices[index].position = (*m_vertexList)[vertexIndex].position;
        vertices[index].texture = (*m_vertexList)[vertexIndex].texture;
        vertices[index].normal = (*m_vertexList)[vertexIndex].normal;
        indices[index] = index;
        // Also store the vertex position information in the node vertex array.
        node->vertexArray[index].x = (*m_vertexList)[vertexIndex].position.x;
        node->vertexArray[index].y = (*m_vertexList)[vertexIndex].position.y;
        node->vertexArray[index].z = (*m_vertexList)[vertexIndex].position.z;
        index++;
        vertexIndex++;

        vertices[index].position = (*m_vertexList)[vertexIndex].position;
        vertices[index].texture = (*m_vertexList)[vertexIndex].texture;
        vertices[index].normal = (*m_vertexList)[vertexIndex].normal;
        indices[index] = index;
        node->vertexArray[index].x = (*m_vertexList)[vertexIndex].position.x;
        node->vertexArray[index].y = (*m_vertexList)[vertexIndex].position.y;
        node->vertexArray[index].z =(*m_vertexList)[vertexIndex].position.z;
        index++;
        vertexIndex++;

        vertices[index].position = (*m_vertexList)[vertexIndex].position;
        vertices[index].texture = (*m_vertexList)[vertexIndex].texture;
        vertices[index].normal = (*m_vertexList)[vertexIndex].normal;
        indices[index] = index;
        node->vertexArray[index].x = (*m_vertexList)[vertexIndex].position.x;
        node->vertexArray[index].y = (*m_vertexList)[vertexIndex].position.y;
        node->vertexArray[index].z = (*m_vertexList)[vertexIndex].position.z;

        index++;
    }
}

// Set up the description of the vertex buffer.
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof(VertexType) * vertexCount;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;

// Give the subresource structure a pointer to the vertex data.
vertexData.pSysMem = vertices;
vertexData.SysMemPitch = 0;
vertexData.SysMemSlicePitch = 0;

// Now finally create the vertex buffer.
device->CreateBuffer(&vertexBufferDesc, &vertexData, &node->vertexBuffer);

// Set up the description of the index buffer.
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(unsigned long) * vertexCount;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
indexBufferDesc.StructureByteStride = 0;

// Give the subresource structure a pointer to the index data.
indexData.pSysMem = indices;
indexData.SysMemPitch = 0;
indexData.SysMemSlicePitch = 0;

// Create the index buffer.
device->CreateBuffer(&indexBufferDesc, &indexData, &node->indexBuffer);

// Release the vertex and index arrays now that the data is stored in the buffers in the node.
delete [] vertices;
vertices = 0;

delete [] indices;
indices = 0;

The isTriangleContained() method checks if the current triangle is in the current node. if it is, then the triangle gets saved in the array wich will then fill the buffer for this node. As you can see, because of the structure mentioned above the selection of the vertices here is quite simple.

The thing is that the m_vertices vector is allocating a huge amount of memory if i try to generate and render a terrain bigger than 2048 for example. Is there a better way to handle all this? Maybe make some calculations on the GPU?

Thanks to anyone willing to help!

1

1 Answers

0
votes

With a terrain size of 2048 it will take 2048 (width) * 2048 (length) * 8 (# floats) * 4 (bytes per float) which is about 128 MB. It's a decent chunk of memory but not unreasonable by itself.

I don't understand what the quadtree code is supposed to do. It looks like you are creating another 2 copies of the geometry? Why do this?