-1

I am writing a simple minecraft clone and I need help. I have a "Chunk" class that implements the "calculateMesh" and "createChunk" methods, the second method fills the chunk blocks field with a specific block type, and then the "calculateMesh" method adds the required edges (if the block is not locked or the block is not transparent). Also, chunks can get a block of another chunk through the "World" class, which stores pointers to all chunks. To form the correct mesh, the createChunk methods for all chunks must complete. Both operations are heavy and they block the main thread if a new chunk was added. So, I thought that I need to add a thread pool to my project. I did it. But I don't know how to synchronize the threads to avoid errors when one chunk is trying to get a block of another unfinished chun . I think all blocks should wait for the createChunk method to complete and then create the mesh. How should I do it? Maybe use a priority queue in my thread pool? (Sorry for my English :))

void Chunk::createChunk() {
    float heightP = 0.0;

    blocks = new Block[CHUNK_VOLUME];

    for (int x = 0; x < CHUNK_X; x++) {
        for (int z = 0; z < CHUNK_Z; z++) {
            glm::vec3 worldPos = toWorldPosition(glm::vec3(x, 1.0f, z));

            heightP = perlin->noise(worldPos.x * 0.03f, worldPos.y * 0.8f, worldPos.z * 0.03f);

            for (int y = 0; y < CHUNK_Y; y++) {

                BlockType blocktype = StoneBlock;

                if (y > heightP * CHUNK_Y * 0.2f) {
                    blocktype = AirBlock;
                }

                addBlock(x, y, z, blocktype);
            }
        }
    }

    chunkReady = true;
}

void Chunk::calculateMesh() {

    if (hasMesh) {
        return;
    }

    vertices.clear();

    for (int x = 0; x < CHUNK_X; x++) {
        for (int z = 0; z < CHUNK_Z; z++) {
            for (int y = 0; y < CHUNK_Y; y++) {
                if (GetBlock(x, y, z).getBlockType() == StoneBlock) {
                    tryAddFace(x, y, z, BackFace);
                    tryAddFace(x, y, z, LeftFace);
                    tryAddFace(x, y, z, RightFace);
                    tryAddFace(x, y, z, FrontFace);
                    tryAddFace(x, y, z, TopFace);
                    tryAddFace(x, y, z, BottomFace);
                }
            }
        }
    }

    hasMesh = true;
}

Block& Chunk::GetBlock(int x, int y, int z) {
    if (outOfBounds(x, y, z)) {
        glm::vec3 pos = toWorldPosition({ x, y, z });
        return world->GetBlock(pos.x, pos.y, pos.z);
    }

    return blocks[getBlockIndex(x, y, z)];
}
BDL
  • 21,052
  • 22
  • 49
  • 55
  • Looking producer-consumer. See if a `std::condition_variable` helps out. Producer can signal that a chunk is ready and the consumer can grab it when it's got time. – user4581301 Jun 05 '21 at 20:23
  • use a std::atomic value and its exchange function to acquire a resource. if a necessary resource is busy (could not be acquired), do another task instead. – Abel Jun 05 '21 at 22:42

1 Answers1

0

You can use std::async to achieve that i think. You should bind the function to std::async object which will call it asynchronously and will return future object. When you get the future return value, it will be attached into the main thread. Here is example usage:

#include <iostream>
#include <functional>
#include <future>

void foo()
{
    std::cout << "Hello async" << std::endl;  
}

int bar()
{
    return 15; // Your return type and value...
}

int main()
{
   std::future<void> f1 = std::async(std::bind(foo));
   std::future<int> f2 = std::async(std::bind(bar));
   
   f1.get();
   std::cout << f2.get() << std::endl;
   return 0;
}
Emre Erdog
  • 23
  • 1
  • 5