#include "planet/PlanetFace.h" #include "planet/Planet.h" PlanetFace::PlanetFace(Planet* planet, const unsigned int face) : m_planet(planet), m_face(face), m_pos(glm::vec3(0.0f, 0.0f, 0.0f)) { // Set face normal depending on face switch (m_face) { case Face::FACE_BOTTOM: m_normal = glm::vec3(0.0f, -1.0f, 0.0f); break; case Face::FACE_TOP: m_normal = glm::vec3(0.0f, 1.0f, 0.0f); break; case Face::FACE_LEFT: m_normal = glm::vec3(-1.0f, 0.0f, 0.0f); break; case Face::FACE_RIGHT: m_normal = glm::vec3(1.0f, 0.0f, 0.0f); break; case Face::FACE_FRONT: m_normal = glm::vec3(0.0f, 0.0f, -1.0f); break; case Face::FACE_BACK: m_normal = glm::vec3(0.0f, 0.0f, 1.0f); break; } m_chunks = new ChunkManager(this, planet->getRadius()); } PlanetFace::~PlanetFace() { } void PlanetFace::draw(Camera* camera, Shader* shader) { if (m_chunks) { glEnable(GL_CULL_FACE); glCullFace(GL_BACK); m_chunks->render(camera, shader); glDisable(GL_CULL_FACE); } } void PlanetFace::tick(Camera* camera, GLfloat dtime) { if (m_chunks) { m_chunks->update(camera, dtime); } } // ******** // // CHUNKS // // ******** // ChunkManager::ChunkManager(PlanetFace* face, GLuint chunkCount) : m_face(face) { m_forceVisibilityUpdate = true; for (int x = 0; x < (int)chunkCount; x++) { for (int y = 0; y < (int)chunkCount; y++) { for (int z = 0; z < (int)chunkCount; z++) { m_chunks.push_back(new Chunk(m_face->getPlanet(), m_face, x * CHUNK_SIZE, y * CHUNK_SIZE, z * CHUNK_SIZE)); } } } } ChunkManager::~ChunkManager() { for(auto iterator = m_chunks.begin(); iterator != m_chunks.end(); ++iterator) { Chunk* pChunk = (*iterator); delete pChunk; } } void ChunkManager::update(Camera* camera, GLfloat dtime) { updateUnload(); updateLoad(); updateRebuild(); updateGenerate(); updateFlags(); updateVisibility(camera); updateRender(camera); } Chunk* ChunkManager::getChunk(int x, int y, int z) { for(auto iterator = m_chunks.begin(); iterator != m_chunks.end(); ++iterator) { Chunk* pChunk = (*iterator); if (pChunk->getX() == x && pChunk->getY() == y && pChunk->getZ() == z) return pChunk; } return NULL; } void ChunkManager::updateUnload() { // Unload any chunks for(auto iterator = m_chunkUnloadQueue.begin(); iterator != m_chunkUnloadQueue.end(); ++iterator) { Chunk* pChunk = (*iterator); if(pChunk->isLoaded()) { pChunk->unload(); m_forceVisibilityUpdate = true; } } // Clear the unload list (every frame) m_chunkUnloadQueue.clear(); } void ChunkManager::updateLoad() { int lNumOfChunksLoaded = 0; for(auto iterator = m_chunkLoadQueue.begin(); iterator != m_chunkLoadQueue.end() && (lNumOfChunksLoaded != ASYNC_NUM_CHUNKS_PER_FRAME); ++iterator) { Chunk* pChunk = (*iterator); if(pChunk->isLoaded() == false) { if(lNumOfChunksLoaded != ASYNC_NUM_CHUNKS_PER_FRAME) { pChunk->load(); // Increase the chunks loaded count lNumOfChunksLoaded++; m_forceVisibilityUpdate = true; } } } // Clear the load list (every frame) m_chunkLoadQueue.clear(); } void ChunkManager::updateRebuild() { // Rebuild any chunks that are in the rebuild chunk list int lNumRebuiltChunkThisFrame = 0; for(auto iterator = m_chunkRebuildQueue.begin(); iterator != m_chunkRebuildQueue.end() && (lNumRebuiltChunkThisFrame != ASYNC_NUM_CHUNKS_PER_FRAME); ++iterator) { Chunk* pChunk = (*iterator); if(pChunk->isLoaded() && pChunk->isGenerated()) { if(lNumRebuiltChunkThisFrame != ASYNC_NUM_CHUNKS_PER_FRAME) { pChunk->rebuildMesh(); // If we rebuild a chunk, add it to the list of chunks that need their render flags updated // since we might now be empty or surrounded m_chunkFlagsQueue.push_back(pChunk); // Also add our neighbours since they might now be surrounded too (If we have neighbours) Chunk* pChunkXMinus = getChunk(pChunk->getX()-1, pChunk->getY(), pChunk->getZ()); Chunk* pChunkXPlus = getChunk(pChunk->getX()+1, pChunk->getY(), pChunk->getZ()); Chunk* pChunkYMinus = getChunk(pChunk->getX(), pChunk->getY()-1, pChunk->getZ()); Chunk* pChunkYPlus = getChunk(pChunk->getX(), pChunk->getY()+1, pChunk->getZ()); Chunk* pChunkZMinus = getChunk(pChunk->getX(), pChunk->getY(), pChunk->getZ()-1); Chunk* pChunkZPlus = getChunk(pChunk->getX(), pChunk->getY(), pChunk->getZ()+1); if(pChunkXMinus != NULL) m_chunkFlagsQueue.push_back(pChunkXMinus); if(pChunkXPlus != NULL) m_chunkFlagsQueue.push_back(pChunkXPlus); if(pChunkYMinus != NULL) m_chunkFlagsQueue.push_back(pChunkYMinus); if(pChunkYPlus != NULL) m_chunkFlagsQueue.push_back(pChunkYPlus); if(pChunkZMinus != NULL) m_chunkFlagsQueue.push_back(pChunkZMinus); if(pChunkZPlus != NULL) m_chunkFlagsQueue.push_back(pChunkZPlus); // Only rebuild a certain number of chunks per frame lNumRebuiltChunkThisFrame++; m_forceVisibilityUpdate = true; } } } // Clear the rebuild list m_chunkRebuildQueue.clear(); } void ChunkManager::updateFlags() { for(auto iterator = m_chunkFlagsQueue.begin(); iterator != m_chunkFlagsQueue.end(); ++iterator) { Chunk* pChunk = (*iterator); pChunk->updateFlags(); } m_chunkFlagsQueue.clear(); } void ChunkManager::updateGenerate() { // Generate any chunks that have not already been generated for(auto iterator = m_chunkGenerateQueue.begin(); iterator != m_chunkGenerateQueue.end(); ++iterator) { Chunk* pChunk = (*iterator); if(pChunk->isLoaded() && pChunk->isGenerated() == false) { pChunk->generate(); if(pChunk->isGenerated()) { // Only force the visibility update if we actually generate the chunk, some chunks wait in the pre-generate stage... m_forceVisibilityUpdate = true; } } } // Clear the generate list (every frame) m_chunkGenerateQueue.clear(); } void ChunkManager::updateVisibility(Camera* camera) { m_forceVisibilityUpdate = false; m_chunksVisible.clear(); for(auto iterator = m_chunks.begin(); iterator != m_chunks.end(); ++iterator) { Chunk* pChunk = (*iterator); // TODO: Temporary distance limit if (glm::abs(glm::length(camera->getPosition() - pChunk->getAbsolutePosition())) > 64.0f) { if (pChunk->isLoaded()) { m_chunkUnloadQueue.push_back(pChunk); continue; } continue; } else { if (!pChunk->isLoaded()) { m_chunkLoadQueue.push_back(pChunk); continue; } } // Generate chunk if (!pChunk->isGenerated()) { m_chunkGenerateQueue.push_back(pChunk); continue; } if (pChunk->isRedrawRequired() || !pChunk->isDetailedEnough(camera)) { m_chunkRebuildQueue.push_back(pChunk); continue; } m_chunksVisible.push_back(pChunk); } } void ChunkManager::updateRender(Camera* camera) { // Clear the render list each frame BEFORE we do our tests to see what chunks should be rendered m_chunkRenderQueue.clear(); for(auto iterator = m_chunksVisible.begin(); iterator != m_chunksVisible.end(); ++iterator) { Chunk* pChunk = (*iterator); if(pChunk != NULL) { if(pChunk->isLoaded() && pChunk->isGenerated()) { if(pChunk->shouldRender()) // Early flags check so we don't always have to do the frustum check... { // TODO: fix frustum culling //glm::vec3 pos = pChunk->getAbsolutePosition(); //glm::mat4 model = glm::translate(glm::mat4(1.0f), pos); //camera->updateFrustum(model); //if(camera->frustumContainsBox(glm::vec3(0.0f), glm::vec3((float)CHUNK_SIZE / 2)) > 0) //{ m_chunkRenderQueue.push_back(pChunk); //} } } } } } void ChunkManager::render(Camera* camera, Shader* shader) { // Render only the chunks that should be rendered for(auto iterator = m_chunkRenderQueue.begin(); iterator != m_chunkRenderQueue.end(); ++iterator) { Chunk* pChunk = (*iterator); pChunk->draw(camera, shader); } }