2016-07-14 2 views
-1

Я работаю над движком вокселей на C++, и после реализации кусков я понял, что они очень дороги для генерации. Таким образом, я не имею в виду заполнение их блоками, я имею в виду создание куска сетки.Создание кусков сетки в движке вокселя медленное

Игра проходит гладко, как только куски генерируются, за исключением размещения и удаления вокселей. Всякий раз, когда изменяется кусок, он реконструируется сеткой. Это дорогостоящий процесс. Для одного фрагмента требуется около 0,36 секунды, что приводит к замораживанию около 0,36 секунды при редактировании фрагмента. Кроме того, из-за этого 0,36-секундного всплеска для одного куска, загрузка мира с большим радиусом куска или 3 или 4 занимает несколько минут. С 4 кусками требуется 189 секунд, (4 * 2)^3 * 0,36 (512 кусков в 0,36 секунды)

Это мой код генерации сетки. Он выполняет итерацию над каждым блоком в куске, и если он не воздух, он добавляет ему вершины куба, иначе игнорирует его. Это позже станет более сложным методом с некоторыми материалами, которые я запланировал, что плохо, если метод уже медленный.

void WorldRenderer::constructChunkMesh(Chunk* chunk) 
{ 
    if (!chunk->isInitialized() || chunk->getNumBlocks() <= 0) 
     return; //If the chunk isn't initialized, or is empty, don't construct anything for it. 

    ChunkMesh mesh; 

    //iterate over every block within the chunk. 
    //CHUNK_SIZE has a value of 16. Each chunk is 16x16x16 blocks. 
    for (int x = 0; x < CHUNK_SIZE; x++) 
    { 
     for (int y = 0; y < CHUNK_SIZE; y++) 
     { 
      for (int z = 0; z < CHUNK_SIZE; z++) 
      { 
       if (chunk->getBlock(x, y, z) != Blocks::BLOCK_TYPE_AIR) //if the block is solid, add vertices, otherwise, don't render it. 
       { 
        //the 8 vertices for a cube. mesh.addVertex(...) returns the index. 
        int i0 = mesh.addVertex(Vertex(glm::vec3(0.0F, 0.0F, 1.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F))); 
        int i1 = mesh.addVertex(Vertex(glm::vec3(1.0F, 0.0F, 1.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F))); 
        int i2 = mesh.addVertex(Vertex(glm::vec3(0.0F, 1.0F, 1.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F))); 
        int i3 = mesh.addVertex(Vertex(glm::vec3(1.0F, 1.0F, 1.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F))); 
        int i4 = mesh.addVertex(Vertex(glm::vec3(0.0F, 0.0F, 0.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F))); 
        int i5 = mesh.addVertex(Vertex(glm::vec3(1.0F, 0.0F, 0.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F))); 
        int i6 = mesh.addVertex(Vertex(glm::vec3(0.0F, 1.0F, 0.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F))); 
        int i7 = mesh.addVertex(Vertex(glm::vec3(1.0F, 1.0F, 0.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F))); 

        //The xyz coord in the iteration in world-relative coordinates, instead of chunk-relative 
        int wx = (chunk->getPos().x * CHUNK_SIZE) + x; 
        int wy = (chunk->getPos().y * CHUNK_SIZE) + y; 
        int wz = (chunk->getPos().z * CHUNK_SIZE) + z; 

        //top  y+ 
        if (World::getBlock(wx, wy + 1, wz) <= 0) 
        { 
         //if a block does not exist in the y+ direction to this one, add the top face. 
         mesh.addFace(i2, i3, i7); 
         mesh.addFace(i2, i7, i6); 
        } 
        //bottom y- 
        if (World::getBlock(wx, wy - 1, wz) <= 0) 
        { 
         //if a block does not exist in the y- direction to this one, add the top face. 
         mesh.addFace(i0, i4, i1); 
         mesh.addFace(i1, i4, i5); 
        } 
        //front  z- 
        if (World::getBlock(wx, wy, wz - 1) <= 0) 
        { 
         //if a block does not exist in the z- direction to this one, add the top face. 
         mesh.addFace(i6, i7, i4); 
         mesh.addFace(i7, i5, i4); 
        } 
        //back  z+ 
        if (World::getBlock(wx, wy, wz + 1) <= 0) 
        { 
         //if a block does not exist in the z+ direction to this one, add the top face. 
         mesh.addFace(i0, i1, i2); 
         mesh.addFace(i1, i3, i2); 
        } 
        //right  x+ 
        if (World::getBlock(wx + 1, wy, wz) <= 0) 
        { 
         //if a block does not exist in the x+ direction to this one, add the top face. 
         mesh.addFace(i1, i7, i3); 
         mesh.addFace(i1, i5, i7); 
        } 
        //left  x- 
        if (World::getBlock(wx - 1, wy, wz) <= 0) 
        { 
         //if a block does not exist in the x- direction to this one, add the top face. 
         mesh.addFace(i2, i6, i4); 
         mesh.addFace(i0, i2, i4); 
        } 
       } 
      } 
     } 
    } 


    //The rest of this is OpenGL code, and doesn't add any significant 
    //performance drop. I have measured this. 
    GeometryData gd = MeshHandler::compileGeometry(mesh.vertices.data(), mesh.indices.data(), mesh.vertices.size(), mesh.indices.size()); 

    RenderableChunk rc; 
    rc.pos = chunk->getPos(); 
    auto a = std::find(chunks.begin(), chunks.end(), rc); 
    int index = a - chunks.begin(); 

    if (a != chunks.end()) 
    { 
     rc = chunks[index]; 
    } 
    else 
    { 
     GLuint VAO; 
     GLuint* VBOs = new GLuint[2]; 

     //1527864 bytes maximum per chunk (1.5MB) 

     glGenVertexArrays(1, &VAO); 
     glBindVertexArray(VAO); 

     glGenBuffers(2, VBOs); 
     glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]); 
     glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * 8 * MAX_BLOCKS, nullptr, GL_DYNAMIC_DRAW); 

     glVertexAttribPointer(ATTRIB_VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, position))); 
     glEnableVertexAttribArray(ATTRIB_VERTEX_ARRAY); 
     glVertexAttribPointer(ATTRIB_NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, normal))); 
     glEnableVertexAttribArray(ATTRIB_NORMAL_ARRAY); 
     glVertexAttribPointer(ATTRIB_COLOUR_ARRAY, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, colour))); 
     glEnableVertexAttribArray(ATTRIB_COLOUR_ARRAY); 
     glVertexAttribPointer(ATTRIB_TEXTURE_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, texture))); 
     glEnableVertexAttribArray(ATTRIB_TEXTURE_ARRAY); 

     glBindBuffer(GL_ARRAY_BUFFER, 0); 

     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBOs[1]); 
     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * 36 * MAX_BLOCKS, nullptr, GL_DYNAMIC_DRAW); 
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 

     glBindVertexArray(0); 

     rc.VAO = VAO; 
     rc.VBOs = VBOs; 
    } 

    rc.numIndices = gd.numIndices; 

    glBindVertexArray(rc.VAO); 

    glBindBuffer(GL_ARRAY_BUFFER, rc.VBOs[0]); 
    glBufferSubData(GL_ARRAY_BUFFER, 0, gd.vboSize(), gd.vertices); 
    glBindBuffer(GL_ARRAY_BUFFER, 0); 

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rc.VBOs[1]); 
    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, gd.iboSize(), gd.indices); 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 

    glBindVertexArray(0); 

    if (index >= 0 && index < chunks.size()) 
    { 
     chunks[index] = rc; 
    } 
    else 
    { 
     chunks.push_back(rc); 
    } 
} 

И структура ChunkMesh используется, когда я считаю, что проблема заключается в:

struct ChunkMesh 
{ 
    std::vector<Vertex> vertices; 
    std::vector<GLushort> indices; 

    int addVertex(Vertex v) 
    { 
     //add a vertex to the mesh, and return its index in the list. 
     vertices.push_back(v); 
     return vertices.size() - 1; 
    } 

    void addFace(int v0, int v1, int v2) 
    { 
     //construct a face with 3 vertices. 
     indices.push_back(v0); 
     indices.push_back(v1); 
     indices.push_back(v2); 
    } 
}; 

Я считаю, что проблема заключается в структуры ChunkMesh с использоваться push_backs. std::vector очень медленный для сотен push_backs, но я не могу найти альтернативы. Что я могу заменить вектор?

Я собираюсь сделать куски полностью неправильными? Как я могу оптимизировать эту функцию?

Любая помощь будет высоко оценена.

Спасибо.

Редактировать: Я попытался зарезервировать векторы, которые, к моему замешательству, не повлияли на производительность. Он остается на 0,36 секунды.

Я добавил конструктор ChunkMesh принять в число блоков, например, так:

ChunkMesh(int numBlocks) 
{ 
    vertices.reserve(numBlocks * 8); //8 vertices per cube 
    indices.reserve(numBlocks * 36); //36 indices per cube 
} 
+1

Вызов резервного аванса должен устранить проблемы с помощью push back * IFF * push_back действительно является проблемой. Профиль, не догадывайтесь. – Borgleader

+0

@Borgleader См. Редактирование. Я пробовал это, и это не сработало. – Kelan

+2

Также рассмотрите возможность использования [emplace_back] (http://en.cppreference.com/w/cpp/container/vector/emplace_back), если сможете. Это должно избегать копий ([короткий пример] (http://coliru.stacked-crooked.com/a/f1ce0c4ad047147f)). Опять же, без точной информации профилирования, действительно сложно оптимизировать что угодно. – Borgleader

ответ

0

Моя рекомендация будет оценить, если вам нужны вершины, которые не находятся на поверхности куска.

Если нет, вам не нужно добавлять их в свой ChunkMesh, что значительно уменьшает количество вершин и push_back вызовов.

+0

Функция выполняет итерацию по каждому кубу, и если блок блокирует любую грань, эта грань не добавляется. Таким образом, в основном это не добавляет вершин, которые не отображаются. – Kelan

+0

индексы для лица блока не добавляются, хотя вы вызываете 'mesh.addVertex()' восемь раз для каждого блока, не проверяя, заблокировано ли лицо или нет. –

Смежные вопросы