diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index bf90f8a93..09544a358 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -11,8 +11,10 @@ #include "world/Level.hpp" #include "window/Camera.hpp" #include "maths/FrustumCulling.hpp" +#include "maths/util.hpp" #include "util/listutil.hpp" #include "util/ObjectsPool.hpp" +#include "util/timeutil.hpp" #include "settings.hpp" #include "content/Content.hpp" @@ -162,76 +164,63 @@ void ChunksRenderer::clear() { threadPool.clearQueue(); } -const ChunkMesh* ChunksRenderer::getOrRender( - const std::shared_ptr& chunk, bool important, bool lowPriority -) { - auto found = meshes.find(glm::ivec2(chunk->x, chunk->z)); - if (found == meshes.end()) { - return render(chunk, important, lowPriority); - } - if (chunk->flags.modified && chunk->flags.lighted) { - render(chunk, important, lowPriority); - } - return &found->second; -} - void ChunksRenderer::update() { threadPool.pullResults(); enqueuedInFrame = 0; -} -const Mesh* ChunksRenderer::retrieveChunk( - size_t index, const Camera& camera, bool culling -) { - auto chunk = chunks.getChunks()[index]; - if (chunk == nullptr) { - return nullptr; - } - if (!chunk->flags.lighted) { - const auto& found = meshes.find({chunk->x, chunk->z}); - if (found == meshes.end()) { - return nullptr; - } else { - return found->second.mesh.get(); + int width = chunks.getWidth(); + int halfWidth = width / 2; + int halfHeight = chunks.getHeight() / 2; + int centerX = chunks.getOffsetX() + halfWidth; + int centerY = chunks.getOffsetY() + halfHeight; + + meshBuildQueue.clear(); + auto& chunksArray = chunks.getChunks(); + for (int index = 0; index < chunks.getVolume(); index++) { + const auto& chunk = chunks.getChunks()[index]; + if (chunk == nullptr || !chunk->flags.lighted) { + continue; + } + int x = chunk->x; + int z = chunk->z; + if (chunk->flags.modified || meshes.find({x, z}) == meshes.end()) { + meshBuildQueue.emplace_back(x - centerX, z - centerY); } } - float distance = glm::distance( - camera.position, - glm::vec3( - (chunk->x + 0.5f) * CHUNK_W, - camera.position.y, - (chunk->z + 0.5f) * CHUNK_D - ) + std::sort( + meshBuildQueue.begin(), + meshBuildQueue.end(), + [](const glm::ivec2& a, const glm::ivec2& b) { + return util::length2(a) < util::length2(b); + } ); - auto mesh = getOrRender( - chunk, - distance < CHUNK_W * 1.5f, - distance > CHUNK_W * settings.chunks.loadDistance.get() * 0.5 - ); - if (mesh == nullptr) { - return nullptr; - } - if (chunk->flags.dirtyHeights) { - chunk->updateHeights(); - } - if (culling) { - const auto& meshAABB = mesh->meshAABB; - auto aabbMin = meshAABB.min(); - auto aabbMax = meshAABB.max(); - glm::vec3 min( - chunk->x * CHUNK_W + std::min(0.0f, aabbMin.x), - chunk->bottom, - chunk->z * CHUNK_D + std::min(0.0f, aabbMin.z) - ); - glm::vec3 max( - chunk->x * CHUNK_W + aabbMax.x, - chunk->top, - chunk->z * CHUNK_D + aabbMax.z - ); - if (!frustum.isBoxVisible(min, max)) return nullptr; + const int topN = 10; + int top = std::min(meshBuildQueue.size(), topN); + for (int i = 0; i < top; i++) { + glm::ivec2 offset = meshBuildQueue[i]; + size_t index = + (offset.y + halfHeight) * width + offset.x + halfWidth; + + auto& chunk = chunksArray[index]; + assert(chunk != nullptr); + + float distance = glm::distance( + glm::vec3 (centerX, 0.0f, centerY), + glm::vec3( + (chunk->x + 0.5f) * CHUNK_W, + 0, + (chunk->z + 0.5f) * CHUNK_D + ) + ); + bool important = distance < CHUNK_W * 1.5f; + bool lowPriority = distance > CHUNK_W * settings.chunks.loadDistance.get() * 0.5; + + if (chunk->flags.dirtyHeights) { + chunk->updateHeights(); + } + render(chunk, important, lowPriority); } - return mesh->mesh.get(); } void ChunksRenderer::drawShadowsPass( @@ -318,16 +307,43 @@ void ChunksRenderer::drawChunks( // TODO: minimize draw calls number for (int i = indices.size()-1; i >= 0; i--) { auto& chunk = chunks.getChunks()[indices[i].index]; - auto mesh = retrieveChunk(indices[i].index, camera, culling); - if (mesh == nullptr) { + if (chunk == nullptr) { continue; } + auto found = meshes.find(glm::ivec2(chunk->x, chunk->z)); + if (found == meshes.end()) { + continue; + } + auto& mesh = found->second; + if (mesh.mesh == nullptr) { + continue; + } + if (culling) { + const auto& meshAABB = mesh.meshAABB; + auto aabbMin = meshAABB.min(); + auto aabbMax = meshAABB.max(); + glm::vec3 min( + chunk->x * CHUNK_W + std::min(0.0f, aabbMin.x), + chunk->bottom, + chunk->z * CHUNK_D + std::min(0.0f, aabbMin.z) + ); + glm::vec3 max( + chunk->x * CHUNK_W + aabbMax.x, + chunk->top, + chunk->z * CHUNK_D + aabbMax.z + ); + + if (!frustum.isBoxVisible(min, max)) { + continue; + } + } + glm::vec3 coord( chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f ); glm::mat4 model = glm::translate(glm::mat4(1.0f), coord); shader.uniformMatrix("u_model", model); - mesh->draw(GL_TRIANGLES, glm::distance2(camera.position * glm::vec3(1, 0, 1), + mesh.mesh->draw(GL_TRIANGLES, glm::distance2(camera.position * glm::vec3(1, 0, 1), (coord + glm::vec3(CHUNK_W * 0.5f, 0.0f, CHUNK_D * 0.5f))) < denseDistance2); visibleChunks++; } diff --git a/src/graphics/render/ChunksRenderer.hpp b/src/graphics/render/ChunksRenderer.hpp index 5e6783d97..2f25875c5 100644 --- a/src/graphics/render/ChunksRenderer.hpp +++ b/src/graphics/render/ChunksRenderer.hpp @@ -1,16 +1,17 @@ #pragma once +#define GLM_ENABLE_EXPERIMENTAL + +#include "util/ThreadPool.hpp" +#include "commons.hpp" + #include #include #include #include -#define GLM_ENABLE_EXPERIMENTAL #include -#include "util/ThreadPool.hpp" -#include "commons.hpp" - template class Mesh; class Chunk; class Level; @@ -44,22 +45,6 @@ struct RendererJob { }; class ChunksRenderer { - const Chunks& chunks; - const Assets& assets; - const Frustum& frustum; - const EngineSettings& settings; - - std::unique_ptr renderer; - std::unordered_map meshes; - std::unordered_map inwork; - std::vector indices; - util::ThreadPool threadPool; - const Mesh* retrieveChunk( - size_t index, const Camera& camera, bool culling - ); - std::shared_ptr prepareVoxelsVolume(const Chunk& chunk); - - size_t enqueuedInFrame = 0; public: ChunksRenderer( const Level& level, @@ -71,16 +56,9 @@ public: ); virtual ~ChunksRenderer(); - const ChunkMesh* render( - const std::shared_ptr& chunk, bool important, bool lowPriority - ); void unload(const Chunk* chunk); void clear(); - const ChunkMesh* getOrRender( - const std::shared_ptr& chunk, bool important, bool lowPriority - ); - void drawShadowsPass( const Camera& camera, Shader& shader, const Camera& playerCamera ); @@ -92,4 +70,25 @@ public: void update(); static size_t visibleChunks; + +private: + const Chunks& chunks; + const Assets& assets; + const Frustum& frustum; + const EngineSettings& settings; + + std::unique_ptr renderer; + std::unordered_map meshes; + std::unordered_map inwork; + std::vector indices; + util::ThreadPool threadPool; + std::vector meshBuildQueue; + + size_t enqueuedInFrame = 0; + + std::shared_ptr prepareVoxelsVolume(const Chunk& chunk); + + const ChunkMesh* render( + const std::shared_ptr& chunk, bool important, bool lowPriority + ); }; diff --git a/src/maths/util.hpp b/src/maths/util.hpp index cb58212eb..c5a576f0d 100644 --- a/src/maths/util.hpp +++ b/src/maths/util.hpp @@ -109,12 +109,16 @@ namespace util { /// @return integer square of vector length /// @note glm::length2 does not support integer vectors - inline int length2(const glm::ivec3& a) { + inline long long int length2(const glm::ivec2& a) { + return a.x * a.x + a.y * a.y; + } + + inline long long int length2(const glm::ivec3& a) { return a.x * a.x + a.y * a.y + a.z * a.z; } /// @return integer square of vector length - inline int length2(int x, int y, int z) { + inline long long int length2(int x, int y, int z) { return x * x + y * y + z * z; }