voxspatium/src/planet/PlanetFace.cpp

339 lines
7.7 KiB
C++

#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);
}
}