diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index b3d6aca0..a8d205b3 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -11,6 +11,7 @@ #include "graphics/ui/elements/InputBindBox.hpp" #include "graphics/render/WorldRenderer.hpp" #include "graphics/render/ParticlesRenderer.hpp" +#include "graphics/render/ChunksRenderer.hpp" #include "logic/scripting/scripting.hpp" #include "objects/Player.hpp" #include "objects/Entities.hpp" @@ -95,7 +96,7 @@ std::shared_ptr create_debug_panel( })); panel->add(create_label([=]() { return L"chunks: "+std::to_wstring(level->chunks->getChunksCount())+ - L" visible: "+std::to_wstring(level->chunks->visible); + L" visible: "+std::to_wstring(ChunksRenderer::visibleChunks); })); panel->add(create_label([=]() { return L"entities: "+std::to_wstring(level->entities->size())+L" next: "+ diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index 656124da..565978f6 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -1,9 +1,17 @@ #include "ChunksRenderer.hpp" #include "BlocksRenderer.hpp" #include "debug/Logger.hpp" +#include "assets/Assets.hpp" #include "graphics/core/Mesh.hpp" +#include "graphics/core/Shader.hpp" +#include "graphics/core/Texture.hpp" +#include "graphics/core/Atlas.hpp" #include "voxels/Chunk.hpp" +#include "voxels/Chunks.hpp" #include "world/Level.hpp" +#include "window/Camera.hpp" +#include "maths/FrustumCulling.hpp" +#include "util/listutil.hpp" #include "settings.hpp" #include @@ -12,6 +20,8 @@ static debug::Logger logger("chunks-render"); +size_t ChunksRenderer::visibleChunks = 0; + class RendererWorker : public util::Worker, RendererResult> { Level* level; BlocksRenderer renderer; @@ -39,9 +49,14 @@ public: ChunksRenderer::ChunksRenderer( Level* level, + const Assets& assets, + const Frustum& frustum, const ContentGfxCache* cache, const EngineSettings* settings ) : level(level), + assets(assets), + frustum(frustum), + settings(settings), threadPool( "chunks-render-pool", [=](){return std::make_shared(level, cache, settings);}, @@ -103,14 +118,84 @@ std::shared_ptr ChunksRenderer::getOrRender(const std::shared_ptr& return found->second; } -std::shared_ptr ChunksRenderer::get(Chunk* chunk) { - auto found = meshes.find(glm::ivec2(chunk->x, chunk->z)); - if (found != meshes.end()) { - return found->second; - } - return nullptr; -} - void ChunksRenderer::update() { threadPool.update(); } + +bool ChunksRenderer::drawChunk( + size_t index, const Camera& camera, Shader& shader, bool culling +) { + auto chunk = level->chunks->getChunks()[index]; + if (chunk == nullptr || !chunk->flags.lighted) { + return false; + } + float distance = glm::distance( + camera.position, + glm::vec3( + (chunk->x + 0.5f) * CHUNK_W, + camera.position.y, + (chunk->z + 0.5f) * CHUNK_D + ) + ); + auto mesh = getOrRender(chunk, distance < CHUNK_W * 1.5f); + if (mesh == nullptr) { + return false; + } + if (culling) { + glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D); + glm::vec3 max( + chunk->x * CHUNK_W + CHUNK_W, + chunk->top, + chunk->z * CHUNK_D + CHUNK_D + ); + + if (!frustum.isBoxVisible(min, max)) return false; + } + 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(); + return true; +} + +void ChunksRenderer::drawChunks( + const Camera& camera, Shader& shader +) { + const auto& chunks = *level->chunks; + const auto& atlas = assets.require("blocks"); + + atlas.getTexture()->bind(); + update(); + + // [warning] this whole method is not thread-safe for chunks + + int chunksWidth = chunks.getWidth(); + int chunksOffsetX = chunks.getOffsetX(); + int chunksOffsetY = chunks.getOffsetY(); + + if (indices.size() != chunks.getVolume()) { + indices.clear(); + for (int i = 0; i < chunks.getVolume(); i++) { + indices.push_back(ChunksSortEntry {i, 0}); + } + } + float px = camera.position.x / static_cast(CHUNK_W) - 0.5f; + float pz = camera.position.z / static_cast(CHUNK_D) - 0.5f; + for (auto& index : indices) { + float x = index.index % chunksWidth + chunksOffsetX - px; + float z = index.index / chunksWidth + chunksOffsetY - pz; + index.d = (x * x + z * z) * 1024; + } + util::insertion_sort(indices.begin(), indices.end()); + + bool culling = settings->graphics.frustumCulling.get(); + + visibleChunks = 0; + //if (GLEW_ARB_multi_draw_indirect && false) { + // TODO: implement Multi Draw Indirect chunks draw + //} else { + for (size_t i = 0; i < indices.size(); i++) { + visibleChunks += drawChunk(indices[i].index, camera, shader, culling); + } + //} +} diff --git a/src/graphics/render/ChunksRenderer.hpp b/src/graphics/render/ChunksRenderer.hpp index 62d07280..4cfa34d4 100644 --- a/src/graphics/render/ChunksRenderer.hpp +++ b/src/graphics/render/ChunksRenderer.hpp @@ -14,10 +14,24 @@ class Mesh; class Chunk; class Level; +class Camera; +class Shader; +class Chunks; +class Assets; +class Frustum; class BlocksRenderer; class ContentGfxCache; struct EngineSettings; +struct ChunksSortEntry { + int index; + int d; + + inline bool operator<(const ChunksSortEntry& o) const noexcept { + return d > o.d; + } +}; + struct RendererResult { glm::ivec2 key; bool cancelled; @@ -26,25 +40,41 @@ struct RendererResult { class ChunksRenderer { Level* level; + 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, RendererResult> threadPool; + + bool drawChunk( + size_t index, const Camera& camera, Shader& shader, bool culling + ); public: ChunksRenderer( - Level* level, + Level* level, + const Assets& assets, + const Frustum& frustum, const ContentGfxCache* cache, const EngineSettings* settings ); virtual ~ChunksRenderer(); - std::shared_ptr render(const std::shared_ptr& chunk, bool important); + std::shared_ptr render( + const std::shared_ptr& chunk, bool important + ); void unload(const Chunk* chunk); void clear(); - std::shared_ptr getOrRender(const std::shared_ptr& chunk, bool important); - std::shared_ptr get(Chunk* chunk); + std::shared_ptr getOrRender( + const std::shared_ptr& chunk, bool important + ); + void drawChunks(const Camera& camera, Shader& shader); void update(); + + static size_t visibleChunks; }; diff --git a/src/graphics/render/ModelBatch.cpp b/src/graphics/render/ModelBatch.cpp index 48133ef9..51c2fe9f 100644 --- a/src/graphics/render/ModelBatch.cpp +++ b/src/graphics/render/ModelBatch.cpp @@ -47,9 +47,9 @@ static glm::mat4 extract_rotation(glm::mat4 matrix) { ModelBatch::ModelBatch( size_t capacity, - Assets* assets, - Chunks* chunks, - const EngineSettings* settings + const Assets& assets, + const Chunks& chunks, + const EngineSettings& settings ) : batch(std::make_unique(capacity)), assets(assets), @@ -73,7 +73,7 @@ void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix, if (mesh.lighting) { glm::vec3 gpos = matrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); gpos += lightsOffset; - lights = MainBatch::sampleLight(gpos, *chunks, backlight); + lights = MainBatch::sampleLight(gpos, chunks, backlight); } for (size_t i = 0; i < vcount / 3; i++) { batch->prepare(3); @@ -107,7 +107,7 @@ void ModelBatch::render() { return a.mesh->texture < b.mesh->texture; } ); - bool backlight = settings->graphics.backlight.get(); + bool backlight = settings.graphics.backlight.get(); for (auto& entry : entries) { draw( *entry.mesh, @@ -136,6 +136,6 @@ void ModelBatch::setTexture(const std::string& name, return setTexture(found->second, varTextures); } } - auto region = util::get_texture_region(*assets, name, "blocks:notfound"); + auto region = util::get_texture_region(assets, name, "blocks:notfound"); batch->setTexture(region.texture, region.region); } diff --git a/src/graphics/render/ModelBatch.hpp b/src/graphics/render/ModelBatch.hpp index 5781baa1..4faf0ea8 100644 --- a/src/graphics/render/ModelBatch.hpp +++ b/src/graphics/render/ModelBatch.hpp @@ -23,10 +23,10 @@ namespace model { using texture_names_map = std::unordered_map; class ModelBatch { - Assets* assets; - Chunks* chunks; + const Assets& assets; + const Chunks& chunks; - const EngineSettings* settings; + const EngineSettings& settings; glm::vec3 lightsOffset {}; static inline glm::vec3 SUN_VECTOR {0.411934f, 0.863868f, -0.279161f}; @@ -39,6 +39,7 @@ class ModelBatch { glm::vec3 tint, const texture_names_map* varTextures, bool backlight); + void setTexture(const std::string& name, const texture_names_map* varTextures); @@ -53,9 +54,9 @@ class ModelBatch { public: ModelBatch( size_t capacity, - Assets* assets, - Chunks* chunks, - const EngineSettings* settings + const Assets& assets, + const Chunks& chunks, + const EngineSettings& settings ); ~ModelBatch(); diff --git a/src/graphics/render/TextsRenderer.cpp b/src/graphics/render/TextsRenderer.cpp index 7fd6c214..349c4a39 100644 --- a/src/graphics/render/TextsRenderer.cpp +++ b/src/graphics/render/TextsRenderer.cpp @@ -10,14 +10,15 @@ #include "graphics/core/Shader.hpp" #include "presets/NotePreset.hpp" -TextsRenderer::TextsRenderer(const Frustum* frustum) : frustum(frustum) { +TextsRenderer::TextsRenderer( + Batch3D& batch, const Assets& assets, const Frustum& frustum +) + : batch(batch), assets(assets), frustum(frustum) { } void TextsRenderer::renderText( - Batch3D& batch, const TextNote& note, const DrawContext& context, - const Assets& assets, const Camera& camera, const EngineSettings& settings, bool hudVisible @@ -54,8 +55,8 @@ void TextsRenderer::renderText( } if (preset.displayMode != NoteDisplayMode::PROJECTED) { - if (!frustum->isBoxVisible(pos - xvec * (width * 0.5f), - pos + xvec * (width * 0.5f))) { + if (!frustum.isBoxVisible(pos - xvec * (width * 0.5f), + pos + xvec * (width * 0.5f))) { return; } } @@ -71,9 +72,7 @@ void TextsRenderer::renderText( } void TextsRenderer::renderTexts( - Batch3D& batch, const DrawContext& context, - const Assets& assets, const Camera& camera, const EngineSettings& settings, bool hudVisible, @@ -96,9 +95,8 @@ void TextsRenderer::renderTexts( shader.uniformMatrix("u_projview", camera.getProjView()); shader.uniformMatrix("u_apply", glm::mat4(1.0f)); batch.begin(); - for (const auto& note : notes) { - renderText(batch, note, context, assets, camera, settings, hudVisible); + renderText(note, context, camera, settings, hudVisible); } batch.flush(); } diff --git a/src/graphics/render/TextsRenderer.hpp b/src/graphics/render/TextsRenderer.hpp index df0de386..57dfa24b 100644 --- a/src/graphics/render/TextsRenderer.hpp +++ b/src/graphics/render/TextsRenderer.hpp @@ -9,24 +9,22 @@ class TextNote; struct EngineSettings; class TextsRenderer { - const Frustum* frustum; + Batch3D& batch; + const Assets& assets; + const Frustum& frustum; void renderText( - Batch3D& batch, const TextNote& note, const DrawContext& context, - const Assets& assets, const Camera& camera, const EngineSettings& settings, bool hudVisible ); public: - TextsRenderer(const Frustum* frustum); + TextsRenderer(Batch3D& batch, const Assets& assets, const Frustum& frustum); void renderTexts( - Batch3D& batch, const DrawContext& context, - const Assets& assets, const Camera& camera, const EngineSettings& settings, bool hudVisible, diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index ed3985d7..31abb248 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -22,7 +22,6 @@ #include "maths/voxmaths.hpp" #include "objects/Entities.hpp" #include "objects/Player.hpp" -#include "util/listutil.hpp" #include "settings.hpp" #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" @@ -51,6 +50,9 @@ #include "Emitter.hpp" #include "TextNote.hpp" +inline constexpr size_t BATCH3D_CAPACITY = 4096; +inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000; + bool WorldRenderer::showChunkBorders = false; bool WorldRenderer::showEntitiesDebug = false; @@ -60,30 +62,28 @@ WorldRenderer::WorldRenderer( : engine(engine), level(frontend->getLevel()), player(player), + assets(*engine->getAssets()), frustumCulling(std::make_unique()), lineBatch(std::make_unique()), + batch3d(std::make_unique(BATCH3D_CAPACITY)), modelBatch(std::make_unique( - 20'000, - engine->getAssets(), - level->chunks.get(), - &engine->getSettings() + MODEL_BATCH_CAPACITY, assets, *level->chunks, engine->getSettings() )), particles(std::make_unique( - *engine->getAssets(), - *frontend->getLevel(), - &engine->getSettings().graphics + assets, *level, &engine->getSettings().graphics )), - texts(std::make_unique(frustumCulling.get())), - guides(std::make_unique()) { - renderer = std::make_unique( - level, frontend->getContentGfxCache(), &engine->getSettings() - ); - batch3d = std::make_unique(4096); - + texts(std::make_unique( + *batch3d, assets, *frustumCulling + )), + guides(std::make_unique()), + chunks(std::make_unique( + level, assets, *frustumCulling, frontend->getContentGfxCache(), &engine->getSettings() + )) +{ auto& settings = engine->getSettings(); level->events->listen( EVT_CHUNK_HIDDEN, - [this](lvl_event_type, Chunk* chunk) { renderer->unload(chunk); } + [this](lvl_event_type, Chunk* chunk) { chunks->unload(chunk); } ); auto assets = engine->getAssets(); skybox = std::make_unique( @@ -94,87 +94,6 @@ WorldRenderer::WorldRenderer( WorldRenderer::~WorldRenderer() = default; -bool WorldRenderer::drawChunk( - size_t index, const Camera& camera, Shader& shader, bool culling -) { - auto chunk = level->chunks->getChunks()[index]; - if (chunk == nullptr || !chunk->flags.lighted) { - return false; - } - float distance = glm::distance( - camera.position, - glm::vec3( - (chunk->x + 0.5f) * CHUNK_W, - camera.position.y, - (chunk->z + 0.5f) * CHUNK_D - ) - ); - auto mesh = renderer->getOrRender(chunk, distance < CHUNK_W * 1.5f); - if (mesh == nullptr) { - return false; - } - if (culling) { - glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D); - glm::vec3 max( - chunk->x * CHUNK_W + CHUNK_W, - chunk->top, - chunk->z * CHUNK_D + CHUNK_D - ); - - if (!frustumCulling->isBoxVisible(min, max)) return false; - } - 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(); - return true; -} - -void WorldRenderer::drawChunks( - Chunks* chunks, const Camera& camera, Shader& shader -) { - auto assets = engine->getAssets(); - auto atlas = assets->get("blocks"); - - atlas->getTexture()->bind(); - renderer->update(); - - // [warning] this whole method is not thread-safe for chunks - - int chunksWidth = chunks->getWidth(); - int chunksOffsetX = chunks->getOffsetX(); - int chunksOffsetY = chunks->getOffsetY(); - - if (indices.size() != chunks->getVolume()) { - indices.clear(); - for (int i = 0; i < chunks->getVolume(); i++) { - indices.push_back(ChunksSortEntry {i, 0}); - } - } - float px = camera.position.x / static_cast(CHUNK_W) - 0.5f; - float pz = camera.position.z / static_cast(CHUNK_D) - 0.5f; - for (auto& index : indices) { - float x = index.index % chunksWidth + chunksOffsetX - px; - float z = index.index / chunksWidth + chunksOffsetY - pz; - index.d = (x * x + z * z) * 1024; - } - util::insertion_sort(indices.begin(), indices.end()); - - bool culling = engine->getSettings().graphics.frustumCulling.get(); - if (culling) { - frustumCulling->update(camera.getProjView()); - } - - chunks->visible = 0; - if (GLEW_ARB_multi_draw_indirect && false) { - // TODO: implement Multi Draw Indirect chunks draw - } else { - for (size_t i = 0; i < indices.size(); i++) { - chunks->visible += drawChunk(indices[i].index, camera, shader, culling); - } - } -} - void WorldRenderer::setupWorldShader( Shader& shader, const Camera& camera, @@ -219,11 +138,7 @@ void WorldRenderer::renderLevel( bool pause, bool hudVisible ) { - const auto& assets = *engine->getAssets(); - - texts->renderTexts( - *batch3d, ctx, assets, camera, settings, hudVisible, false - ); + texts->renderTexts(ctx, camera, settings, hudVisible, false); bool culling = engine->getSettings().graphics.frustumCulling.get(); float fogFactor = @@ -233,6 +148,10 @@ void WorldRenderer::renderLevel( setupWorldShader(entityShader, camera, settings, fogFactor); skybox->bind(); + if (culling) { + frustumCulling->update(camera.getProjView()); + } + level->entities->render( assets, *modelBatch, @@ -246,7 +165,7 @@ void WorldRenderer::renderLevel( auto& shader = assets.require("main"); setupWorldShader(shader, camera, settings, fogFactor); - drawChunks(level->chunks.get(), camera, shader); + chunks->drawChunks(camera, shader); if (!pause) { scripting::on_frontend_render(); @@ -302,7 +221,7 @@ void WorldRenderer::renderLines( } void WorldRenderer::renderHands( - const Camera& camera, const Assets& assets, float delta + const Camera& camera, float delta ) { auto& entityShader = assets.require("entity"); auto indices = level->content->getIndices(); @@ -408,11 +327,11 @@ void WorldRenderer::draw( } renderLines(camera, linesShader, ctx); if (player->currentCamera == player->fpCamera) { - renderHands(camera, assets, delta * !pause); + renderHands(camera, delta * !pause); } } } - renderBlockOverlay(wctx, assets); + renderBlockOverlay(wctx); } // Rendering fullscreen quad with @@ -423,7 +342,7 @@ void WorldRenderer::draw( postProcessing->render(pctx, screenShader); } -void WorldRenderer::renderBlockOverlay(const DrawContext& wctx, const Assets& assets) { +void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) { int x = std::floor(player->currentCamera->position.x); int y = std::floor(player->currentCamera->position.y); int z = std::floor(player->currentCamera->position.z); @@ -469,5 +388,5 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx, const Assets& as } void WorldRenderer::clear() { - renderer->clear(); + chunks->clear(); } diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index 6dd61fca..974c626c 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -22,7 +22,6 @@ class TextsRenderer; class Shader; class Frustum; class Engine; -class Chunks; class LevelFrontend; class Skybox; class PostProcessing; @@ -36,39 +35,26 @@ namespace model { struct Model; } -struct ChunksSortEntry { - int index; - int d; - - inline bool operator<(const ChunksSortEntry& o) const noexcept { - return d > o.d; - } -}; - class WorldRenderer { Engine* engine; Level* level; Player* player; + const Assets& assets; std::unique_ptr frustumCulling; std::unique_ptr lineBatch; - std::unique_ptr renderer; + std::unique_ptr batch3d; + std::unique_ptr chunks; std::unique_ptr texts; std::unique_ptr guides; std::unique_ptr skybox; - std::unique_ptr batch3d; std::unique_ptr modelBatch; - - std::vector indices; float timer = 0.0f; - bool drawChunk(size_t index, const Camera& camera, Shader& shader, bool culling); - void drawChunks(Chunks* chunks, const Camera& camera, Shader& shader); - /// @brief Render block selection lines void renderBlockSelection(); - void renderHands(const Camera& camera, const Assets& assets, float delta); + void renderHands(const Camera& camera, float delta); /// @brief Render lines (selection and debug) /// @param camera active camera @@ -77,7 +63,7 @@ class WorldRenderer { const Camera& camera, Shader& linesShader, const DrawContext& pctx ); - void renderBlockOverlay(const DrawContext& context, const Assets& assets); + void renderBlockOverlay(const DrawContext& context); void setupWorldShader( Shader& shader, diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index d2440210..38a5be8c 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -40,10 +40,8 @@ class Chunks { ); util::AreaMap2D, int32_t> areaMap; -public: - size_t visible = 0; WorldFiles* worldFiles; - +public: Chunks( int32_t w, int32_t d,