From 78e3bc11e6bb4093944b951a67102a0e62176ce6 Mon Sep 17 00:00:00 2001 From: EmaMaker Date: Sat, 29 Apr 2023 14:54:43 +0200 Subject: [PATCH] initial update and render with concurrent DS --- include/camera.hpp | 11 +- include/chunk.hpp | 8 +- include/chunkmanager.hpp | 19 +-- include/renderer.hpp | 4 + src/chunk.cpp | 19 +-- src/chunkgenerator.cpp | 1 + src/chunkmanager.cpp | 297 ++++++--------------------------------- src/chunkmesher.cpp | 9 +- src/main.cpp | 17 +-- src/renderer.cpp | 120 ++++++++++------ 10 files changed, 168 insertions(+), 337 deletions(-) diff --git a/include/camera.hpp b/include/camera.hpp index 198536b..ce5c591 100644 --- a/include/camera.hpp +++ b/include/camera.hpp @@ -6,7 +6,7 @@ #include #include -#include +#include class Camera { @@ -38,6 +38,9 @@ public: if (glfwGetKey(window, GLFW_KEY_Z) == GLFW_PRESS) this->cameraPos -= cameraSpeed * cameraUp; + posX = cameraPos.x; + posY = cameraPos.y; + posZ = cameraPos.z; direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); direction.y = sin(glm::radians(pitch)); @@ -78,6 +81,10 @@ public: glm::mat4 getView() { return view; } glm::mat4 getProjection() { return projection; } + float getAtomicPosX() { return posX; } + float getAtomicPosY() { return posY; } + float getAtomicPosZ() { return posZ; } + // Plane extraction as per Gribb&Hartmann // 6 planes, each with 4 components (a,b,c,d) void getFrustumPlanes(glm::vec4 planes[6], bool normalize) @@ -114,6 +121,8 @@ private: float lastX = 400, lastY = 300; float yaw, pitch; + + std::atomic posX, posY, posZ; }; #endif diff --git a/include/chunk.hpp b/include/chunk.hpp index 180b903..bde004e 100644 --- a/include/chunk.hpp +++ b/include/chunk.hpp @@ -27,6 +27,8 @@ namespace Chunk constexpr uint8_t CHUNK_STATE_MESHED = 2; constexpr uint8_t CHUNK_STATE_MESH_LOADED = 4; constexpr uint8_t CHUNK_STATE_LOADED = 8; + constexpr uint8_t CHUNK_STATE_OUTOFVISION = 16; + constexpr uint8_t CHUNK_STATE_UNLOADED = 32; constexpr uint8_t CHUNK_STATE_EMPTY = 64; int coord3DTo1D(int x, int y, int z); @@ -39,6 +41,9 @@ namespace Chunk ~Chunk(); public: + void createBuffers(); + void deleteBuffers(); + glm::vec3 getPosition() { return this->position; } uint8_t getTotalState() { return this->state; } bool getState(uint8_t n) { return (this->state & n) == n; } @@ -52,9 +57,8 @@ namespace Chunk public: GLuint VAO{0}, VBO{0}, EBO{0}, colorBuffer{0}, vIndex{0}; + std::atomic unload_timer{0}; - std::mutex mutex_state; - std::vector vertices; std::vector colors; std::vector indices; diff --git a/include/chunkmanager.hpp b/include/chunkmanager.hpp index 5661dd3..9216d05 100644 --- a/include/chunkmanager.hpp +++ b/include/chunkmanager.hpp @@ -1,33 +1,24 @@ #ifndef CHUNKMANAGER_H #define CHUNKMANAGER_H -// Second to be passed outside of render distance for a chunk to be destroyed +// Seconds to be passed outside of render distance for a chunk to be destroyed #define UNLOAD_TIMEOUT 10 -#include #include + #include "chunk.hpp" #include "globals.hpp" namespace chunkmanager { - std::thread initGenThread(); - std::thread initMeshThread(); - void stopGenThread(); - void stopMeshThread(); - - void mesh(); - void generate(); - - void init(); + std::thread init(); void blockpick(bool place); uint32_t calculateIndex(uint16_t i, uint16_t j, uint16_t k); + void stop(); void destroy(); - std::unordered_map& getChunks(); std::array, chunks_volume>& getChunksIndices(); - void update(float deltaTime); - void updateChunk(uint32_t, uint16_t, uint16_t, uint16_t); + void update(); } #endif diff --git a/include/renderer.hpp b/include/renderer.hpp index a59e783..546ea50 100644 --- a/include/renderer.hpp +++ b/include/renderer.hpp @@ -1,6 +1,9 @@ #ifndef RENDERER_H #define RENDERER_H +#include + +#include "chunk.hpp" #include "shader.hpp" namespace renderer{ @@ -8,6 +11,7 @@ namespace renderer{ void render(); void destroy(); Shader* getRenderShader(); + oneapi::tbb::concurrent_unordered_set& getChunksToRender(); }; diff --git a/src/chunk.cpp b/src/chunk.cpp index d621a44..821b9db 100644 --- a/src/chunk.cpp +++ b/src/chunk.cpp @@ -19,27 +19,30 @@ namespace Chunk { this->position = pos; this->setState(CHUNK_STATE_EMPTY, true); + } + Chunk ::~Chunk() + { + vertices.clear(); + indices.clear(); + colors.clear(); + this->deleteBuffers(); + } + + void Chunk::createBuffers(){ glGenVertexArrays(1, &(this->VAO)); glGenBuffers(1, &(this->colorBuffer)); glGenBuffers(1, &(this->VBO)); glGenBuffers(1, &(this->EBO)); - mutex_state.unlock(); } - Chunk ::~Chunk() - { + void Chunk::deleteBuffers(){ glDeleteBuffers(1, &(this->colorBuffer)); glDeleteBuffers(1, &(this->VBO)); glDeleteBuffers(1, &(this->EBO)); glDeleteVertexArrays(1, &(this->VAO)); - vertices.clear(); - indices.clear(); - colors.clear(); - - mutex_state.unlock(); } Block Chunk::getBlock(int x, int y, int z) diff --git a/src/chunkgenerator.cpp b/src/chunkgenerator.cpp index c80a0c6..1ebec6b 100644 --- a/src/chunkgenerator.cpp +++ b/src/chunkgenerator.cpp @@ -84,6 +84,7 @@ void generateNoise(Chunk::Chunk *chunk) } chunk->setBlocks(block_prev_start, CHUNK_VOLUME, block_prev); + chunk->setState(Chunk::CHUNK_STATE_GENERATED, true); } void generateNoise3D(Chunk::Chunk *chunk) { diff --git a/src/chunkmanager.cpp b/src/chunkmanager.cpp index 2245326..aa63d6e 100644 --- a/src/chunkmanager.cpp +++ b/src/chunkmanager.cpp @@ -1,105 +1,36 @@ +#include "chunkmanager.hpp" + +#include +#include +#include + #include #include -#include +#include #include "chunk.hpp" #include "chunkgenerator.hpp" -#include "chunkmanager.hpp" #include "chunkmesher.hpp" #include "globals.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include +#include "renderer.hpp" namespace chunkmanager { - std::unordered_map chunks; + typedef oneapi::tbb::concurrent_hash_map ChunkTable; + ChunkTable chunks; + + //std::unordered_map chunks; std::array, chunks_volume> chunks_indices; - // thread management - std::mutex mutex_queue_generate; - std::mutex mutex_queue_mesh; - std::set to_generate; - std::set to_mesh; - std::atomic_bool generate_should_run; - std::atomic_bool mesh_should_run; - - // update variables - uint8_t f = 0; - int rr{RENDER_DISTANCE * RENDER_DISTANCE}; - glm::vec3 cameraPos; - int chunkX, chunkY, chunkZ; - - // disposal - std::unordered_map to_delete; - std::set to_delete_delete; - - void mesh() - { - while (mesh_should_run) - if (mutex_queue_mesh.try_lock()) - { - for (const auto &c : to_mesh) - { - if (c->mutex_state.try_lock()) - { - chunkmesher::mesh(c); - c->setState(Chunk::CHUNK_STATE_MESHED, true); - c->mutex_state.unlock(); - } - } - to_mesh.clear(); - mutex_queue_mesh.unlock(); - } - } - - void generate() - { - while (generate_should_run) - if (mutex_queue_generate.try_lock()) - { - for (const auto &c : to_generate) - { - if (c->mutex_state.try_lock()) - { - generateChunk(c); - c->setState(Chunk::CHUNK_STATE_GENERATED, true); - c->mutex_state.unlock(); - } - } - to_generate.clear(); - mutex_queue_generate.unlock(); - } - } - - std::thread initMeshThread() - { - mesh_should_run = true; - std::thread mesh_thread(mesh); - return mesh_thread; - } - - std::thread initGenThread() - { - generate_should_run = true; - std::thread gen_thread(generate); - return gen_thread; - } - - void init(){ + std::atomic_bool should_run; + std::thread init(){ int index{0}; + int rr{RENDER_DISTANCE * RENDER_DISTANCE}; int xp{0}, x{0}; bool b = true; - // Iterate over all chunks, in concentric spheres starting fron the player and going - // outwards. Alternate left and right + // Iterate over all chunks, in concentric spheres starting fron the player and going outwards. Alternate left and right // Eq. of the sphere (x - a)² + (y - b)² + (z - c)² = r² while (xp <= RENDER_DISTANCE) { @@ -131,168 +62,36 @@ namespace chunkmanager } else b = false; } + + should_run = true; + std::thread update_thread (update); + return update_thread; } - void update(float deltaTime) - { - // Try to lock resources - f = 0; - f |= mutex_queue_generate.try_lock(); - f |= mutex_queue_mesh.try_lock() << 1; + std::vector chunks_todelete; + void update(){ + while(should_run) { + //cameraPos = theCamera.getPos(); + int chunkX=static_cast(theCamera.getAtomicPosX() / CHUNK_SIZE); + int chunkY=static_cast(theCamera.getAtomicPosY() / CHUNK_SIZE); + int chunkZ=static_cast(theCamera.getAtomicPosZ() / CHUNK_SIZE); - cameraPos = theCamera.getPos(); - chunkX=static_cast(cameraPos.x) / CHUNK_SIZE; - chunkY=static_cast(cameraPos.y) / CHUNK_SIZE; - chunkZ=static_cast(cameraPos.z) / CHUNK_SIZE; + // Update other chunks + for(int i = 0; i < chunks_volume; i++) { + const uint16_t x = chunks_indices[i][0] + chunkX; + const uint16_t y = chunks_indices[i][1] + chunkY; + const uint16_t z = chunks_indices[i][2] + chunkZ; + const uint32_t index = calculateIndex(x, y, z); - // Use time in float to be consistent with glfw - float currentTime = glfwGetTime(); + ChunkTable::accessor a; + if(!chunks.find(a, index)) chunks.emplace(a, std::make_pair(index, new Chunk::Chunk(glm::vec3(x,y,z)))); - // Check for far chunks that need to be cleaned up from memory - int nUnloaded{0}; - for(const auto& n : chunks){ - Chunk::Chunk* c = n.second; - int x{(int)(c->getPosition().x)}; - int y{(int)(c->getPosition().y)}; - int z{(int)(c->getPosition().z)}; - if( (chunkX-x)*(chunkX-x) + (chunkY-y)*(chunkY-y) + (chunkZ-z)*(chunkZ-z) >= - (int)(RENDER_DISTANCE*1.5)*(int)(RENDER_DISTANCE*1.5)) - if(to_delete.find(n.first) == to_delete.end()) - to_delete.insert(std::make_pair(n.first, currentTime)); - } - for(const auto& n :to_delete){ - if( currentTime>=n.second + UNLOAD_TIMEOUT) { - delete chunks.at(n.first); - chunks.erase(n.first); - nUnloaded++; + if(! (a->second->getState(Chunk::CHUNK_STATE_GENERATED))) generateChunk(a->second); + if(! (a->second->getState(Chunk::CHUNK_STATE_MESHED))) chunkmesher::mesh(a->second); - // Delete afterwards to avoid exception due to invalid iterators - to_delete_delete.insert(n.first); - } - } - for(uint32_t i : to_delete_delete) to_delete.erase(i); - to_delete_delete.clear(); - if(nUnloaded) std::cout << "Unloaded " << nUnloaded << " chunks\n"; + renderer::getChunksToRender().insert(a->second); - for(int i = 0; i < chunks_volume; i++) - updateChunk(calculateIndex(chunks_indices[i][0] + chunkX, - chunks_indices[i][1] + chunkY, - chunks_indices[i][2] + chunkZ), - chunks_indices[i][0] + chunkX, - chunks_indices[i][1] + chunkY, - chunks_indices[i][2] + chunkZ); - - // Unlock mutexes if previously locked. Unlocking a mutex not locked by the current thread - // or already locked is undefined behaviour, so checking has to be done - if ((f & 1)) - mutex_queue_generate.unlock(); - if ((f & 2)) - mutex_queue_mesh.unlock(); - } - - // Generation and meshing happen in two separate threads from the main one - // Chunk states are used to decide which actions need to be done on the chunk and sets+mutexes - // to pass the chunks to be operated on between the threads. - // Uploading data to GPU still needs to be done in the main thread, or another OpenGL context - // needs to be opened, which further complicates stuff. - void updateChunk(uint32_t index, uint16_t i, uint16_t j, uint16_t k) - { - if (chunks.find(index) == chunks.end()) - { - Chunk::Chunk *c = new Chunk::Chunk(glm::vec3(i, j, k)); - chunks.insert(std::make_pair(index, c)); - } - else - { - Chunk::Chunk *c = chunks.at(index); - - if (!(c->mutex_state.try_lock())) - return; - - if (!c->getState(Chunk::CHUNK_STATE_GENERATED)) - { - if (f & 1) - to_generate.insert(c); - } - else - { - if (!c->getState(Chunk::CHUNK_STATE_MESHED)) - { - if (f & 2) - to_mesh.insert(c); - } - else - { - if (!c->getState(Chunk::CHUNK_STATE_MESH_LOADED)) chunkmesher::sendtogpu(c); - } - } - c->mutex_state.unlock(); - } - } - - void blockpick(bool place){ - // cast a ray from the camera in the direction pointed by the camera itself - glm::vec3 pos = cameraPos; - for(float t = 0.0; t <= 10.0; t += 0.5){ - // traverse the ray a block at the time - pos = theCamera.getPos() + t * theCamera.getFront(); - - // get which chunk and block the ray is at - int px = ((int)(pos.x))/CHUNK_SIZE; - int py = ((int)(pos.y))/CHUNK_SIZE; - int pz = ((int)(pos.z))/CHUNK_SIZE; - int bx = pos.x - px*CHUNK_SIZE; - int by = pos.y - py*CHUNK_SIZE; - int bz = pos.z - pz*CHUNK_SIZE; - - // exit early if the position is invalid or the chunk does not exist - if(px < 0 || py < 0 || pz < 0) return; - if(chunks.find(calculateIndex(px, py, pz)) == chunks.end()) return; - - Chunk::Chunk* c = chunks.at(calculateIndex(px, py, pz)); - Block b = c->getBlock(bx, by, bz); - - // if the block is non empty - if(b != Block::AIR){ - - // if placing a new block - if(place){ - // Go half a block backwards on the ray, to check the block where the ray was - // coming from - // Doing this and not using normal adds the unexpected (and unwanted) ability to - // place blocks diagonally, without faces colliding with the block that has - // been clicked - pos -= theCamera.getFront()*0.5f; - - int px1 = ((int)(pos.x))/CHUNK_SIZE; - int py1 = ((int)(pos.y))/CHUNK_SIZE; - int pz1 = ((int)(pos.z))/CHUNK_SIZE; - int bx1 = pos.x - px1*CHUNK_SIZE; - int by1 = pos.y - py1*CHUNK_SIZE; - int bz1 = pos.z - pz1*CHUNK_SIZE; - - // exit early if the position is invalid or the chunk does not exist - if(px1 < 0 || py1 < 0 || pz1 < 0) return; - if(chunks.find(calculateIndex(px1, py1, pz1)) == chunks.end()) return; - - Chunk::Chunk* c1 = chunks.at(calculateIndex(px1, py1, pz1)); - // place the new block (only stone for now) - c1->setBlock( Block::STONE, bx1, by1, bz1); - - // update the mesh of the chunk - chunkmesher::mesh(c1); - // mark the mesh of the chunk the be updated on the gpu - c1->setState(Chunk::CHUNK_STATE_MESH_LOADED, false); - }else{ - // replace the current block with air to remove it - c->setBlock( Block::AIR, bx, by, bz); - - // update the mesh of the chunk - chunkmesher::mesh(c); - // mark the mesh of the chunk the be updated on the gpu - c->setState(Chunk::CHUNK_STATE_MESH_LOADED, false); - } - break; + a.release(); } } } @@ -302,21 +101,13 @@ namespace chunkmanager return i | (j << 10) | (k << 20); } - std::unordered_map& getChunks(){ return chunks; } std::array, chunks_volume>& getChunksIndices(){ return chunks_indices; } - void destroy() - { - for (auto &n : chunks) - delete n.second; - } - - void stopGenThread(){ - generate_should_run = false; - } - - void stopMeshThread(){ - mesh_should_run = false; + void stop() { should_run=false; } + void destroy(){ + /*for(const auto& n : chunks){ + delete n.second; + }*/ } }; diff --git a/src/chunkmesher.cpp b/src/chunkmesher.cpp index b3d4e56..061365a 100755 --- a/src/chunkmesher.cpp +++ b/src/chunkmesher.cpp @@ -35,12 +35,16 @@ void mesh(Chunk::Chunk* chunk) chunk->vIndex = 0; // Abort if chunk is empty - if(chunk->getState(Chunk::CHUNK_STATE_EMPTY)) return; + if(chunk->getState(Chunk::CHUNK_STATE_EMPTY)){ + chunk->setState(Chunk::CHUNK_STATE_MESHED, true); + return; + } // convert tree to array since it is easier to work with it int length{0}; std::unique_ptr blocks = chunk->getBlocksArray(&length); if(length == 0) { + chunk->setState(Chunk::CHUNK_STATE_MESHED, true); return; } @@ -180,13 +184,14 @@ void mesh(Chunk::Chunk* chunk) } } } + + chunk->setState(Chunk::CHUNK_STATE_MESHED, true); } void sendtogpu(Chunk::Chunk* chunk) { if (chunk->vIndex > 0) { - // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). glBindVertexArray(chunk->VAO); diff --git a/src/main.cpp b/src/main.cpp index 51e61f1..a8df144 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -63,9 +63,7 @@ int main() SpaceFilling::initLUT(); renderer::init(); - chunkmanager::init(); - std::thread genThread = chunkmanager::initGenThread(); - std::thread meshThread = chunkmanager::initMeshThread(); + std::thread chunkmanager_thread = chunkmanager::init(); while (!glfwWindowShouldClose(window)) { @@ -94,9 +92,6 @@ int main() // Reset blockping timeout if 200ms have passed if(glfwGetTime() - lastBlockPick > 0.1) blockpick = false; - // ChunkManager - chunkmanager::update(deltaTime); - // Render pass renderer::render(); @@ -106,10 +101,8 @@ int main() } // Stop threads and wait for them to finish - chunkmanager::stopGenThread(); - chunkmanager::stopMeshThread(); - genThread.join(); - meshThread.join(); + chunkmanager::stop(); + chunkmanager_thread.join(); // Cleanup allocated memory chunkmanager::destroy(); @@ -136,13 +129,13 @@ void processInput(GLFWwindow *window) glfwSetWindowShouldClose(window, true); if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2) == GLFW_PRESS && !blockpick){ - chunkmanager::blockpick(false); + //chunkmanager::blockpick(false); blockpick=true; lastBlockPick=glfwGetTime(); } if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS && !blockpick){ - chunkmanager::blockpick(true); + //chunkmanager::blockpick(true); blockpick=true; lastBlockPick=glfwGetTime(); } diff --git a/src/renderer.cpp b/src/renderer.cpp index b2450a8..8214eb0 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -3,14 +3,18 @@ #include "chunkmanager.hpp" #include "chunkmesher.hpp" #include "globals.hpp" - #include "stb_image.h" namespace renderer{ + oneapi::tbb::concurrent_unordered_set chunks_torender; + oneapi::tbb::concurrent_unordered_set render_todelete; + Shader* theShader; GLuint chunkTexture; Shader* getRenderShader() { return theShader; } + oneapi::tbb::concurrent_unordered_set& getChunksToRender(){ return chunks_torender; } + void init(){ // Create Shader @@ -40,63 +44,89 @@ namespace renderer{ int total{0}, toGpu{0}; glm::vec4 frustumPlanes[6]; theCamera.getFrustumPlanes(frustumPlanes, true); - - glm::vec3 cameraPos = theCamera.getPos(); - int chunkX=static_cast(cameraPos.x) / CHUNK_SIZE; - int chunkY=static_cast(cameraPos.y) / CHUNK_SIZE; - int chunkZ=static_cast(cameraPos.z) / CHUNK_SIZE; + glm::vec3 cameraPos = theCamera.getPos(); + glm::vec3 cameraChunkPos = cameraPos / static_cast(CHUNK_SIZE); theShader->use(); theShader->setVec3("viewPos", cameraPos); - for(int i = 0; i < chunks_volume; i++) { - Chunk::Chunk* c = chunkmanager::getChunks().at(chunkmanager::calculateIndex(chunkmanager::getChunksIndices()[i][0] + - chunkX, chunkmanager::getChunksIndices()[i][1] + chunkY, chunkmanager::getChunksIndices()[i][2] + chunkZ)); - // Frustum Culling of chunk - total++; - glm::vec3 chunk = c->getPosition(); - glm::vec4 chunkW = glm::vec4(chunk.x*static_cast(CHUNK_SIZE), chunk.y*static_cast(CHUNK_SIZE), chunk.z*static_cast(CHUNK_SIZE),1.0); - glm::mat4 model = glm::translate(glm::mat4(1.0), ((float)CHUNK_SIZE) * chunk); + for(Chunk::Chunk* c : chunks_torender){ + if(! (c->getState(Chunk::CHUNK_STATE_MESHED))) continue; - // Check if all the corners of the chunk are outside any of the planes - // TODO (?) implement frustum culling as per (Inigo Quilez)[https://iquilezles.org/articles/frustumcorrect/], and check each - // plane against each corner of the chunk - bool out=false; - int a{0}; - for(int p = 0; p < 6; p++){ - a = 0; - for(int i = 0; i < 8; i++) a += glm::dot(frustumPlanes[p], glm::vec4(chunkW.x + ((float)(i & 1))*CHUNK_SIZE, chunkW.y - + ((float)((i & 2) >> 1))*CHUNK_SIZE, chunkW.z + ((float)((i & 4) >> 2))*CHUNK_SIZE, 1.0)) < 0.0; - - if(a==8){ - out=true; - break; - } + // If the mesh is ready send it to the gpu + if(! (c->getState(Chunk::CHUNK_STATE_MESH_LOADED))){ + if(c->VAO == 0) c->createBuffers(); + chunkmesher::sendtogpu(c); + c->setState(Chunk::CHUNK_STATE_MESH_LOADED, true); } - if (!out) - { - toGpu++; + if(glm::distance(c->getPosition(), cameraChunkPos) < static_cast(RENDER_DISTANCE)){ + // if chunk can be seen + // reset out-of-vision and unload flags + c->setState(Chunk::CHUNK_STATE_OUTOFVISION, false); + c->setState(Chunk::CHUNK_STATE_UNLOADED, false); - if(c->getState(Chunk::CHUNK_STATE_MESH_LOADED) && c->vIndex > 0) - { - // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // wireframe mode - theShader->setMat4("model", model); - theShader->setMat4("view", theCamera.getView()); - theShader->setMat4("projection", theCamera.getProjection()); + // Perform frustum culling and eventually render + glm::vec3 chunk = c->getPosition(); + glm::vec4 chunkW = glm::vec4(chunk.x*static_cast(CHUNK_SIZE), chunk.y*static_cast(CHUNK_SIZE), chunk.z*static_cast(CHUNK_SIZE),1.0); + glm::mat4 model = glm::translate(glm::mat4(1.0), ((float)CHUNK_SIZE) * chunk); - glBindVertexArray(c->VAO); - glDrawElements(GL_TRIANGLES, c->vIndex , GL_UNSIGNED_INT, 0); - glBindVertexArray(0); + // Check if all the corners of the chunk are outside any of the planes + // TODO (?) implement frustum culling as per (Inigo Quilez)[https://iquilezles.org/articles/frustumcorrect/], and check each + // plane against each corner of the chunk + bool out=false; + int a{0}; + for(int p = 0; p < 6; p++){ + a = 0; + for(int i = 0; i < 8; i++) a += glm::dot(frustumPlanes[p], glm::vec4(chunkW.x + ((float)(i & 1))*CHUNK_SIZE, chunkW.y + + ((float)((i & 2) >> 1))*CHUNK_SIZE, chunkW.z + ((float)((i & 4) >> 2))*CHUNK_SIZE, 1.0)) < 0.0; + + if(a==8){ + out=true; + break; + } } + + if (!out) + { + if(c->vIndex > 0) + { + // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // wireframe mode + theShader->setMat4("model", model); + theShader->setMat4("view", theCamera.getView()); + theShader->setMat4("projection", theCamera.getProjection()); + + glBindVertexArray(c->VAO); + glDrawElements(GL_TRIANGLES, c->vIndex , GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + } + } + + }else{ + // When the chunk is outside render distance + + if(c->getState(Chunk::CHUNK_STATE_OUTOFVISION) && glfwGetTime() - + c->unload_timer > UNLOAD_TIMEOUT){ + // If chunk was already out and enough time has passed + // Mark the chunk to be unloaded + c->setState(Chunk::CHUNK_STATE_UNLOADED, true); + // And delete it from the render set + render_todelete.insert(c); + } else{ + // Mark has out of vision and annotate when it started + c->setState(Chunk::CHUNK_STATE_OUTOFVISION, true); + c->setState(Chunk::CHUNK_STATE_UNLOADED, false); + c->unload_timer = glfwGetTime(); + } + } } - - //std::cout << "Chunks to mesh: " << to_mesh.size() << "\n"; - //std::cout << "Chunks to generate: " << to_generate.size() << "\n"; - //std::cout << "Total chunks to draw: " << total << ". Sent to GPU: " << toGpu << "\n"; + + for(Chunk::Chunk* i : render_todelete){ + chunks_torender.unsafe_erase(i); + } + render_todelete.clear(); } - void destroy(){ delete theShader; }