From 3fc109721764c383db1a7eb54c6c5485fbdbbda4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 18 Jun 2026 21:27:28 +0300 Subject: [PATCH 01/52] update WorldRenderer header --- src/graphics/render/WorldRenderer.hpp | 47 ++++++++++++++------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index c3e6b1669..d472b7b75 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -44,6 +44,30 @@ struct CompileTimeShaderSettings { }; class WorldRenderer { +public: + static bool showChunkBorders; + static bool showEntitiesDebug; + + WorldRenderer(Engine& engine, LevelFrontend& frontend, Player& player); + ~WorldRenderer(); + + void update(const Camera& camera, float delta); + + void renderFrame( + const DrawContext& context, + Camera& camera, + bool hudVisible, + PostProcessing& postProcessing + ); + + void clear(); + + void setDebug(bool flag); + + void toggleLightsDebug(); + + Weather& getWeather(); +private: Engine& engine; const Level& level; Player& player; @@ -106,27 +130,4 @@ public: std::unique_ptr texts; std::unique_ptr blockWraps; std::unique_ptr skeletons; - - static bool showChunkBorders; - static bool showEntitiesDebug; - - WorldRenderer(Engine& engine, LevelFrontend& frontend, Player& player); - ~WorldRenderer(); - - void update(const Camera& camera, float delta); - - void renderFrame( - const DrawContext& context, - Camera& camera, - bool hudVisible, - PostProcessing& postProcessing - ); - - void clear(); - - void setDebug(bool flag); - - void toggleLightsDebug(); - - Weather& getWeather(); }; From af71d5b674a7454ca1be77e0af2f2fda90957564 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 18 Jun 2026 22:12:22 +0300 Subject: [PATCH 02/52] simplify settings-based shaders recompilation --- src/graphics/core/Shader.cpp | 3 +- src/graphics/render/WorldRenderer.cpp | 99 ++++++++++++++------------- src/graphics/render/WorldRenderer.hpp | 13 ++-- 3 files changed, 58 insertions(+), 57 deletions(-) diff --git a/src/graphics/core/Shader.cpp b/src/graphics/core/Shader.cpp index 8cc6f2025..1c752ef5a 100644 --- a/src/graphics/core/Shader.cpp +++ b/src/graphics/core/Shader.cpp @@ -186,7 +186,8 @@ void Shader::recompile(const std::vector& defines) { glDeleteProgram(id); id = newProgram; uniformLocations.clear(); - logger.info() << "shader " << id << " has been recompiled"; + logger.debug() << "shader program (" << vertexSource.file << ", " + << fragmentSource.file << ") has been recompiled"; } std::unique_ptr Shader::create( diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 57cc90f24..e59d4392f 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -52,6 +52,7 @@ #include "world/Level.hpp" #include "world/LevelEvents.hpp" #include "world/World.hpp" +#include "debug/Logger.hpp" #include #include @@ -62,9 +63,20 @@ inline constexpr size_t BATCH3D_CAPACITY = 4096; inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000; +static debug::Logger logger("world-renderer"); + bool WorldRenderer::showChunkBorders = false; bool WorldRenderer::showEntitiesDebug = false; +template +static ObserverHandler observe_setting( + ObservableSetting& setting, bool& dirtyShaders +) { + return setting.observe([&dirtyShaders](auto const&) { + dirtyShaders = true; + }); +} + WorldRenderer::WorldRenderer( Engine& engine, LevelFrontend& frontend, Player& player ) @@ -120,10 +132,41 @@ WorldRenderer::WorldRenderer( shadowMapping = std::make_unique(level); debugLines = std::make_unique(level); cloudsRenderer = std::make_unique(); + + keepAlive(observe_setting(settings.graphics.advancedRender, dirtyShaders)); + keepAlive(observe_setting(settings.graphics.shadowsQuality, dirtyShaders)); + keepAlive(observe_setting(settings.graphics.ssao, dirtyShaders)); } WorldRenderer::~WorldRenderer() = default; +void WorldRenderer::refreshSettings() { + Shader* affectedShaders[] { + &assets.require("main"), + &assets.require("entity"), + &assets.require("clouds"), + &assets.require("translucent"), + &assets.require("deferred_lighting").getShader(), + nullptr + }; + + const auto& graphics = engine.getSettings().graphics; + gbufferPipeline = graphics.advancedRender.get(); + + int shadowsQuality = graphics.shadowsQuality.get() * gbufferPipeline; + shadowMapping->setQuality(shadowsQuality); + + std::vector defines; + if (shadowsQuality != 0) defines.emplace_back("ENABLE_SHADOWS"); + if (graphics.ssao.get()) defines.emplace_back("ENABLE_SSAO"); + if (gbufferPipeline) defines.emplace_back("ADVANCED_RENDER"); + + logger.info() << "recompiling shaders due to settings change"; + for (size_t i = 0; affectedShaders[i]; i++) { + affectedShaders[i]->recompile(defines); + } +} + static void setup_weather(Shader& shader, const Weather& weather) { shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity()); shader.uniform1f("u_weatherFogDencity", weather.fogDencity()); @@ -282,33 +325,6 @@ void WorldRenderer::renderLines( } } -void WorldRenderer::refreshSettings(Shader** shaders) { - const auto& graphics = engine.getSettings().graphics; - gbufferPipeline = graphics.advancedRender.get(); - - int shadowsQuality = graphics.shadowsQuality.get() * gbufferPipeline; - shadowMapping->setQuality(shadowsQuality); - - CompileTimeShaderSettings currentSettings { - gbufferPipeline, - shadowsQuality != 0, - graphics.ssao.get() && gbufferPipeline}; - if (prevCTShaderSettings.advancedRender != currentSettings.advancedRender || - prevCTShaderSettings.shadows != currentSettings.shadows || - prevCTShaderSettings.ssao != currentSettings.ssao) { - std::vector defines; - if (currentSettings.shadows) defines.emplace_back("ENABLE_SHADOWS"); - if (currentSettings.ssao) defines.emplace_back("ENABLE_SSAO"); - if (currentSettings.advancedRender) - defines.emplace_back("ADVANCED_RENDER"); - - for (size_t i = 0; shaders[i]; i++) { - shaders[i]->recompile(defines); - } - prevCTShaderSettings = currentSettings; - } -} - void WorldRenderer::update(const Camera& camera, float delta) { timer += delta; weather.update(delta); @@ -327,28 +343,12 @@ void WorldRenderer::renderFrame( const auto& vp = pctx.getViewport(); camera.setAspectRatio(vp.x / static_cast(vp.y)); - - auto& mainShader = assets.require("main"); - auto& entityShader = assets.require("entity"); - auto& cloudsShader = assets.require("clouds"); - auto& translucentShader = assets.require("translucent"); - auto& deferredShader = - assets.require("deferred_lighting").getShader(); - - const auto& settings = engine.getSettings(); - - Shader* affectedShaders[] { - &mainShader, - &entityShader, - &cloudsShader, - &translucentShader, - &deferredShader, - nullptr}; - - refreshSettings(affectedShaders); + if (dirtyShaders) { + refreshSettings(); + dirtyShaders = false; + } const auto& worldInfo = world->getInfo(); - float clouds = weather.clouds(); clouds = glm::max(worldInfo.fog, clouds); float mie = 1.0f + glm::max(worldInfo.fog, clouds * 0.5f) * 2.0f; @@ -371,6 +371,7 @@ void WorldRenderer::renderFrame( setupWorldShader(shader, shadowCamera, engine.getSettings(), 0.0f); chunksRenderer->drawShadowsPass(shadowCamera, shader, camera); }); + const auto& settings = engine.getSettings(); { DrawContext wctx = pctx.sub(); postProcessing.use(wctx, gbufferPipeline); @@ -388,10 +389,13 @@ void WorldRenderer::renderFrame( } float fogFactor = calcFogFactor(); if (gbufferPipeline) { + auto& deferredShader = + assets.require("deferred_lighting").getShader(); deferredShader.use(); setupWorldShader(deferredShader, camera, settings, fogFactor); postProcessing.renderDeferredShading(pctx, assets, timer, camera); } + auto& entityShader = assets.require("entity"); { DrawContext ctx = pctx.sub(); ctx.setDepthTest(true); @@ -423,6 +427,7 @@ void WorldRenderer::renderFrame( wctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); // Translucent blocks { + auto& translucentShader = assets.require("translucent"); auto sctx = wctx.sub(); sctx.setCullFace(true); translucentShader.use(); diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index d472b7b75..e813edb0b 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -6,6 +6,7 @@ #include "presets/WeatherPreset.hpp" #include "world/Weather.hpp" #include "window/Camera.hpp" +#include "util/ObjectsKeeper.hpp" #include #include @@ -37,13 +38,7 @@ class TextsRenderer; class CloudsRenderer; struct EngineSettings; -struct CompileTimeShaderSettings { - bool advancedRender = false; - bool shadows = false; - bool ssao = false; -}; - -class WorldRenderer { +class WorldRenderer final : public util::ObjectsKeeper { public: static bool showChunkBorders; static bool showEntitiesDebug; @@ -90,7 +85,7 @@ private: bool lightsDebug = false; bool gbufferPipeline = false; - CompileTimeShaderSettings prevCTShaderSettings {}; + bool dirtyShaders = false; /// @brief Render block selection lines void renderBlockSelection(); @@ -122,7 +117,7 @@ private: bool hudVisible ); - void refreshSettings(Shader** shaders); + void refreshSettings(); float calcFogFactor() const; public: From 07c35f001cca05ca1699a7887b732ab81566ee41 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 18 Jun 2026 22:14:16 +0300 Subject: [PATCH 03/52] update workflows --- .github/workflows/appimage.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows-clang.yml | 2 +- .github/workflows/windows.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index f43254bd3..b52b8cd16 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main", "dev" ] + branches: [ "main", "dev", "environments" ] workflow_call: inputs: build_name: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index c3f5156f9..a8966db56 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main", "dev" ] + branches: [ "main", "dev", "environments" ] workflow_call: inputs: build_name: diff --git a/.github/workflows/windows-clang.yml b/.github/workflows/windows-clang.yml index 5dd91a576..dd5186362 100644 --- a/.github/workflows/windows-clang.yml +++ b/.github/workflows/windows-clang.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main", "dev" ] + branches: [ "main", "dev", "environments" ] workflow_call: inputs: build_name: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index f248b6ab6..208fcd8c3 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main", "dev" ] + branches: [ "main", "dev", "environments" ] jobs: build-windows: From 22f09d365979685a83f19607325b3ff52448eca8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 19 Jun 2026 00:01:14 +0300 Subject: [PATCH 04/52] start splitting render pipelines & remove the obsolete world fog parameter --- src/frontend/debug_panel.cpp | 6 - src/graphics/core/Shadows.cpp | 2 +- src/graphics/render/DebugLinesRenderer.cpp | 2 +- src/graphics/render/DebugLinesRenderer.hpp | 2 +- src/graphics/render/WorldRenderer.cpp | 317 +++++++++++++-------- src/graphics/render/WorldRenderer.hpp | 32 ++- src/world/World.cpp | 6 - src/world/World.hpp | 3 - 8 files changed, 223 insertions(+), 147 deletions(-) diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index 780bbf00a..ba0cc2205 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -310,12 +310,6 @@ std::shared_ptr create_debug_panel( bar->setConsumer([&](double val) { worldInfo.daytime = val; }); panel->add(bar); } - if (allowDebugCheats) { - auto bar = std::make_shared(gui, 0.0f, 1.0f, 0.0f, 0.005f, 8); - bar->setSupplier([&]() { return worldInfo.fog; }); - bar->setConsumer([&](double val) { worldInfo.fog = val; }); - panel->add(bar); - } { auto checkbox = std::make_shared( gui, L"Show Chunk Borders", glm::vec2(400, 24) diff --git a/src/graphics/core/Shadows.cpp b/src/graphics/core/Shadows.cpp index c9ecc108d..593e4cb4e 100644 --- a/src/graphics/core/Shadows.cpp +++ b/src/graphics/core/Shadows.cpp @@ -100,7 +100,7 @@ void Shadows::setup(Shader& shader, const Weather& weather) { return; } const auto& worldInfo = level.getWorld()->getInfo(); - float cloudsIntensity = glm::max(worldInfo.fog, weather.clouds()); + float cloudsIntensity = weather.clouds(); float shadowsOpacity = 1.0f - cloudsIntensity; shadowsOpacity *= glm::sqrt(glm::abs( glm::mod((worldInfo.daytime + 0.5f) * 2.0f, 1.0f) * 2.0f - 1.0f diff --git a/src/graphics/render/DebugLinesRenderer.cpp b/src/graphics/render/DebugLinesRenderer.cpp index df0b1af78..3750b2cd0 100644 --- a/src/graphics/render/DebugLinesRenderer.cpp +++ b/src/graphics/render/DebugLinesRenderer.cpp @@ -93,7 +93,7 @@ void DebugLinesRenderer::drawCoordSystem( } void DebugLinesRenderer::render( - DrawContext& pctx, + const DrawContext& pctx, const Camera& camera, LineBatch& linesBatch, Shader& linesShader, diff --git a/src/graphics/render/DebugLinesRenderer.hpp b/src/graphics/render/DebugLinesRenderer.hpp index 7a9f0a324..bc6609c5a 100644 --- a/src/graphics/render/DebugLinesRenderer.hpp +++ b/src/graphics/render/DebugLinesRenderer.hpp @@ -20,7 +20,7 @@ public: /// @param linesShader Shader used for rendering lines /// @param showChunkBorders Whether to show chunk borders void render( - DrawContext& ctx, + const DrawContext& ctx, const Camera& camera, LineBatch& linesBatch, Shader& linesShader, diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index e59d4392f..353c5fabd 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -70,10 +70,10 @@ bool WorldRenderer::showEntitiesDebug = false; template static ObserverHandler observe_setting( - ObservableSetting& setting, bool& dirtyShaders + ObservableSetting& setting, bool& dirtySettings ) { - return setting.observe([&dirtyShaders](auto const&) { - dirtyShaders = true; + return setting.observe([&dirtySettings](auto const&) { + dirtySettings = true; }); } @@ -133,9 +133,9 @@ WorldRenderer::WorldRenderer( debugLines = std::make_unique(level); cloudsRenderer = std::make_unique(); - keepAlive(observe_setting(settings.graphics.advancedRender, dirtyShaders)); - keepAlive(observe_setting(settings.graphics.shadowsQuality, dirtyShaders)); - keepAlive(observe_setting(settings.graphics.ssao, dirtyShaders)); + keepAlive(observe_setting(settings.graphics.advancedRender, dirtySettings)); + keepAlive(observe_setting(settings.graphics.shadowsQuality, dirtySettings)); + keepAlive(observe_setting(settings.graphics.ssao, dirtySettings)); } WorldRenderer::~WorldRenderer() = default; @@ -255,12 +255,12 @@ void WorldRenderer::renderOpaque( modelBatch->render(); particles->render(camera); - auto& shader = assets.require("main"); + auto& mainShader = assets.require("main"); auto& cloudsShader = assets.require("clouds"); - setupWorldShader(shader, camera, settings, fogFactor); + setupWorldShader(mainShader, camera, settings, fogFactor); - chunksRenderer->drawChunks(camera, shader); + chunksRenderer->drawChunks(camera, mainShader); blockWraps->draw(ctx); if (level.environment.sky.clouds) { @@ -275,7 +275,7 @@ void WorldRenderer::renderOpaque( if (hudVisible) { auto& linesShader = assets.require("lines"); - renderLines(camera, linesShader, ctx); + renderInWorldLines(camera, linesShader, ctx); } } @@ -308,7 +308,7 @@ void WorldRenderer::renderBlockSelection() { lineBatch->flush(); } -void WorldRenderer::renderLines( +void WorldRenderer::renderInWorldLines( const Camera& camera, Shader& linesShader, const DrawContext& pctx ) { linesShader.use(); @@ -327,159 +327,224 @@ void WorldRenderer::renderLines( void WorldRenderer::update(const Camera& camera, float delta) { timer += delta; + chunksRenderer->update(); weather.update(delta); precipitation->update(delta); particles->update(camera, delta); } +void WorldRenderer::renderFrameClassic( + const DrawContext& pctx, + Camera& camera, + bool hudVisible, + PostProcessing& postProcessing +) { + const auto& worldInfo = level.getWorld()->getInfo(); + + DrawContext ctx = pctx.sub(); + ctx.setDepthTest(true); + ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); + ctx.setFramebuffer(postProcessing.getFramebuffer()); + + skybox->draw( + level.environment, ctx, camera, worldInfo.daytime, weather.clouds() + ); + + if (debug && hudVisible) { + renderDebugLines(ctx, camera); + } + + DrawContext wctx = ctx.sub(); + wctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap()); + wctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); + // Translucent blocks + { + const auto& settings = engine.getSettings(); + auto& translucentShader = assets.require("translucent"); + auto sctx = wctx.sub(); + sctx.setCullFace(true); + translucentShader.use(); + setupWorldShader(translucentShader, camera, settings, calcFogFactor()); + chunksRenderer->drawSortedMeshes(camera, translucentShader); + } + + renderWeatherEffects(camera); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void WorldRenderer::renderFrameAdvanced( + const DrawContext& pctx, + Camera& camera, + bool hudVisible, + PostProcessing& postProcessing +) { + const auto& settings = engine.getSettings(); + const auto& worldInfo = level.getWorld()->getInfo(); + float fogFactor = calcFogFactor(); + + shadowMapping->refresh(camera, pctx, [this, &camera](Camera& shadowCamera) { + auto& shader = assets.require("shadows"); + setupWorldShader(shader, shadowCamera, engine.getSettings(), 0.0f); + chunksRenderer->drawShadowsPass(shadowCamera, shader, camera); + }); + + auto& deferredShader = + assets.require("deferred_lighting").getShader(); + deferredShader.use(); + setupWorldShader(deferredShader, camera, settings, fogFactor); + postProcessing.renderDeferredShading(pctx, assets, timer, camera); + + DrawContext ctx = pctx.sub(); + ctx.setDepthTest(true); + ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); + + postProcessing.bindDepthBuffer(); + + // Background sky plane + skybox->draw( + level.environment, ctx, camera, worldInfo.daytime, weather.clouds() + ); + if (debug && hudVisible) { + renderDebugLines(ctx, camera); + } + + DrawContext wctx = ctx.sub(); + wctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap()); + wctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); + // Translucent blocks + { + const auto& settings = engine.getSettings(); + auto& translucentShader = assets.require("translucent"); + auto sctx = wctx.sub(); + sctx.setCullFace(true); + translucentShader.use(); + setupWorldShader(translucentShader, camera, settings, fogFactor); + chunksRenderer->drawSortedMeshes(camera, translucentShader); + } + + renderWeatherEffects(camera); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + void WorldRenderer::renderFrame( const DrawContext& pctx, Camera& camera, bool hudVisible, PostProcessing& postProcessing ) { - auto projView = camera.getProjView(); - auto world = level.getWorld(); - const auto& vp = pctx.getViewport(); camera.setAspectRatio(vp.x / static_cast(vp.y)); - if (dirtyShaders) { + if (dirtySettings) { refreshSettings(); - dirtyShaders = false; + dirtySettings = false; } - const auto& worldInfo = world->getInfo(); + const auto& worldInfo = level.getWorld()->getInfo(); float clouds = weather.clouds(); - clouds = glm::max(worldInfo.fog, clouds); - float mie = 1.0f + glm::max(worldInfo.fog, clouds * 0.5f) * 2.0f; float random = rand() / static_cast(RAND_MAX); skybox->refresh( level.environment, pctx, worldInfo.daytime, - mie, + 1.0f + clouds, weather.skyTint(), weather.highlight * random, 4 ); - - chunksRenderer->update(); - - shadowMapping->refresh(camera, pctx, [this, &camera](Camera& shadowCamera) { - auto& shader = assets.require("shadows"); - setupWorldShader(shader, shadowCamera, engine.getSettings(), 0.0f); - chunksRenderer->drawShadowsPass(shadowCamera, shader, camera); - }); - const auto& settings = engine.getSettings(); - { - DrawContext wctx = pctx.sub(); - postProcessing.use(wctx, gbufferPipeline); - display::clearDepth(); - - /* Main opaque pass (GBuffer pass) */ { - DrawContext ctx = wctx.sub(); - ctx.setDepthTest(true); - ctx.setCullFace(true); - ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap()); - ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); - renderOpaque(ctx, camera, settings, hudVisible); - } - texts->render(pctx, camera, settings, hudVisible, true); - } - float fogFactor = calcFogFactor(); + renderOpaquePass(pctx, camera, hudVisible, postProcessing); if (gbufferPipeline) { - auto& deferredShader = - assets.require("deferred_lighting").getShader(); - deferredShader.use(); - setupWorldShader(deferredShader, camera, settings, fogFactor); - postProcessing.renderDeferredShading(pctx, assets, timer, camera); - } - auto& entityShader = assets.require("entity"); - { - DrawContext ctx = pctx.sub(); - ctx.setDepthTest(true); - ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); - - if (gbufferPipeline) { - postProcessing.bindDepthBuffer(); - } else { - ctx.setFramebuffer(postProcessing.getFramebuffer()); - } - - // Background sky plane - skybox->draw( - level.environment, ctx, camera, worldInfo.daytime, clouds - ); - - auto& linesShader = assets.require("lines"); - linesShader.use(); - if (debug && hudVisible) { - debugLines->render( - ctx, camera, *lineBatch, linesShader, showChunkBorders - ); - } - linesShader.uniformMatrix("u_projview", projView); - lineBatch->flush(); - - DrawContext wctx = ctx.sub(); - wctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap()); - wctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); - // Translucent blocks - { - auto& translucentShader = assets.require("translucent"); - auto sctx = wctx.sub(); - sctx.setCullFace(true); - translucentShader.use(); - setupWorldShader(translucentShader, camera, settings, fogFactor); - chunksRenderer->drawSortedMeshes(camera, translucentShader); - } - - // Weather effects - entityShader.use(); - setupWorldShader(entityShader, camera, settings, fogFactor); - - std::array weatherInstances { - &weather.a, &weather.b}; - for (const auto& weather : weatherInstances) { - float maxIntensity = weather->fall.maxIntensity; - float zero = weather->fall.minOpacity; - float one = weather->fall.maxOpacity; - float t = (weather->intensity * (one - zero)) * maxIntensity + zero; - entityShader.uniform1i("u_alphaClip", weather->fall.opaque); - entityShader.uniform1f( - "u_opacity", weather->fall.opaque ? t * t : t - ); - if (weather->intensity > 1.e-3f && !weather->fall.texture.empty()) { - precipitation->render(camera, *weather); - } - } - glBindFramebuffer(GL_FRAMEBUFFER, 0); + renderFrameAdvanced(pctx, camera, hudVisible, postProcessing); + } else { + renderFrameClassic(pctx, camera, hudVisible, postProcessing); } postProcessing.render(pctx, assets, timer, camera); if (player.currentCamera == player.fpCamera) { - DrawContext ctx = pctx.sub(); - ctx.setDepthTest(true); - ctx.setCullFace(true); - ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap()); - ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); - display::clearDepth(); - - // prepare modified HUD camera - Camera hudcam = camera; - hudcam.far = 10.0f; - hudcam.setFov(0.9f); - hudcam.position = {}; - - setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f); - hands->render(camera); + renderHandsPass(pctx, camera); } renderBlockOverlay(pctx); glActiveTexture(GL_TEXTURE0); } + +void WorldRenderer::renderOpaquePass( + const DrawContext& pctx, + Camera& camera, + bool hudVisible, + PostProcessing& postProcessing +) { + const auto& settings = engine.getSettings(); + + DrawContext wctx = pctx.sub(); + postProcessing.use(wctx, gbufferPipeline); + display::clearDepth(); + + /* Main opaque pass (GBuffer pass) */ { + DrawContext ctx = wctx.sub(); + ctx.setDepthTest(true); + ctx.setCullFace(true); + ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap()); + ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); + renderOpaque(ctx, camera, settings, hudVisible); + } + texts->render(wctx, camera, settings, hudVisible, true); +} + +void WorldRenderer::renderWeatherEffects(Camera& camera) { + const auto& settings = engine.getSettings(); + auto& entityShader = assets.require("entity"); + entityShader.use(); + setupWorldShader(entityShader, camera, settings, calcFogFactor()); + + std::array weatherInstances { + &weather.a, &weather.b}; + for (const auto& weather : weatherInstances) { + float maxIntensity = weather->fall.maxIntensity; + float zero = weather->fall.minOpacity; + float one = weather->fall.maxOpacity; + float t = (weather->intensity * (one - zero)) * maxIntensity + zero; + entityShader.uniform1i("u_alphaClip", weather->fall.opaque); + entityShader.uniform1f( + "u_opacity", weather->fall.opaque ? t * t : t + ); + if (weather->intensity > 1.e-3f && !weather->fall.texture.empty()) { + precipitation->render(camera, *weather); + } + } +} + +void WorldRenderer::renderDebugLines(const DrawContext& ctx, Camera& camera) { + auto& linesShader = assets.require("lines"); + linesShader.use(); + debugLines->render( + ctx, camera, *lineBatch, linesShader, showChunkBorders + ); + linesShader.uniformMatrix("u_projview", camera.getProjView()); + lineBatch->flush(); +} + +void WorldRenderer::renderHandsPass(const DrawContext& pctx, Camera& camera) { + auto& entityShader = assets.require("entity"); + + DrawContext ctx = pctx.sub(); + ctx.setDepthTest(true); + ctx.setCullFace(true); + ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap()); + ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); + display::clearDepth(); + + // prepare modified HUD camera + Camera hudcam = camera; + hudcam.far = 10.0f; + hudcam.setFov(0.9f); + hudcam.position = {}; + + setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f); + hands->render(camera); +} + void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) { int x = std::floor(player.currentCamera->position.x); int y = std::floor(player.currentCamera->position.y); diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index e813edb0b..d56c21cbf 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -10,7 +10,6 @@ #include #include -#include #include class Assets; @@ -85,7 +84,7 @@ private: bool lightsDebug = false; bool gbufferPipeline = false; - bool dirtyShaders = false; + bool dirtySettings = false; /// @brief Render block selection lines void renderBlockSelection(); @@ -93,7 +92,7 @@ private: /// @brief Render lines (selection and debug) /// @param camera active camera /// @param linesShader shader used - void renderLines( + void renderInWorldLines( const Camera& camera, Shader& linesShader, const DrawContext& pctx ); @@ -117,6 +116,33 @@ private: bool hudVisible ); + void renderOpaquePass( + const DrawContext& context, + Camera& camera, + bool hudVisible, + PostProcessing& postProcessing + ); + + void renderWeatherEffects(Camera& camera); + + void renderHandsPass(const DrawContext& pctx, Camera& camera); + + void renderDebugLines(const DrawContext& context, Camera& camera); + + void renderFrameClassic( + const DrawContext& context, + Camera& camera, + bool hudVisible, + PostProcessing& postProcessing + ); + + void renderFrameAdvanced( + const DrawContext& context, + Camera& camera, + bool hudVisible, + PostProcessing& postProcessing + ); + void refreshSettings(); float calcFogFactor() const; diff --git a/src/world/World.cpp b/src/world/World.cpp index 2acc7edaa..8bbc78b94 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -216,9 +216,6 @@ void WorldInfo::deserialize(const dv::value& root) { daytimeSpeed = timeobj["day-time-speed"].asNumber(); totalTime = timeobj["total-time"].asNumber(); } - if (root.has("weather")) { - fog = root["weather"]["fog"].asNumber(); - } nextInventoryId = root["next-inventory-id"].asInteger(2); nextEntityId = root["next-entity-id"].asInteger(1); root.at("next-player-id").get(nextPlayerId); @@ -244,9 +241,6 @@ dv::value WorldInfo::serialize() const { timeobj["day-time-speed"] = daytimeSpeed; timeobj["total-time"] = totalTime; - root["weather"] = dv::object(); - root["weather"]["fog"] = fog; - root["next-inventory-id"] = nextInventoryId; root["next-entity-id"] = nextEntityId; root["next-player-id"] = nextPlayerId; diff --git a/src/world/World.hpp b/src/world/World.hpp index 14649fb1b..7af7bd1db 100644 --- a/src/world/World.hpp +++ b/src/world/World.hpp @@ -39,9 +39,6 @@ struct WorldInfo : public Serializable { /// @brief total time passed in the world (not depending on daytimeSpeed) double totalTime = 0.0; - /// @brief will be replaced with weather in future - float fog = 0.0f; - entityid_t nextEntityId = 0; int major = 0, minor = -1; From ac00c561ee6796a46dbdb46f2a1fda92726f2a97 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 19 Jun 2026 00:01:45 +0300 Subject: [PATCH 05/52] move weather from WorldRenderer to LevelFrontend --- src/frontend/LevelFrontend.cpp | 4 ++++ src/frontend/LevelFrontend.hpp | 4 ++++ src/frontend/screens/LevelScreen.cpp | 10 +++++----- src/graphics/render/Decorator.cpp | 1 + src/graphics/render/WorldRenderer.cpp | 4 +++- src/graphics/render/WorldRenderer.hpp | 7 +++---- src/logic/scripting/lua/libs/libweather.cpp | 6 ++++++ 7 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/frontend/LevelFrontend.cpp b/src/frontend/LevelFrontend.cpp index d70f09ba2..2143a4778 100644 --- a/src/frontend/LevelFrontend.cpp +++ b/src/frontend/LevelFrontend.cpp @@ -147,3 +147,7 @@ const ContentGfxCache& LevelFrontend::getContentGfxCache() const { LevelController& LevelFrontend::getController() const { return controller; } + +Weather& LevelFrontend::getWeather() { + return weather; +} diff --git a/src/frontend/LevelFrontend.hpp b/src/frontend/LevelFrontend.hpp index 451f88675..28c8a5832 100644 --- a/src/frontend/LevelFrontend.hpp +++ b/src/frontend/LevelFrontend.hpp @@ -1,5 +1,7 @@ #pragma once +#include "world/Weather.hpp" + #include class Assets; @@ -15,6 +17,7 @@ class LevelFrontend { LevelController& controller; Assets& assets; std::unique_ptr contentCache; + Weather weather {}; public: LevelFrontend( Engine& engine, @@ -28,4 +31,5 @@ public: const ContentGfxCache& getContentGfxCache() const; ContentGfxCache& getContentGfxCache(); LevelController& getController() const; + Weather& getWeather(); }; diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 33b637531..613384b7b 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -81,7 +81,7 @@ LevelScreen::LevelScreen( auto resetChunks = [=](bool) { player->chunks->saveAndClear(); - renderer->clear(); + renderer->resetCache(); }; keepAlive(settings.graphics.backlight.observe(resetChunks)); keepAlive(settings.graphics.softLighting.observe(resetChunks)); @@ -94,7 +94,7 @@ LevelScreen::LevelScreen( })); keepAlive(input.addCallback(BIND_CHUNKS_RELOAD, [=]() { player->chunks->saveAndClear(); - renderer->clear(); + renderer->resetCache(); return false; })); controller->preQuitCallbacks.listen([this]() { @@ -153,7 +153,7 @@ void LevelScreen::loadDecorations() { } auto data = io::read_object(CLIENT_FILE); if (data.has("weather")) { - renderer->getWeather().deserialize(data["weather"]); + frontend->getWeather().deserialize(data["weather"]); } } @@ -161,7 +161,7 @@ void LevelScreen::saveDecorations() { io::create_directory("world:client"); auto data = dv::object(); - data["weather"] = renderer->getWeather().serialize(); + data["weather"] = frontend->getWeather().serialize(); io::write_json(CLIENT_FILE, data, true); } @@ -257,7 +257,7 @@ void LevelScreen::update(float delta) { hud->update(hudVisible); - const Weather& weather = renderer->getWeather(); + const Weather& weather = frontend->getWeather(); const Player& player = playerController->getPlayer(); const Camera& camera = *player.currentCamera; decorator->update(paused ? 0.0f : delta, camera, weather); diff --git a/src/graphics/render/Decorator.cpp b/src/graphics/render/Decorator.cpp index b988898ce..4e2a79518 100644 --- a/src/graphics/render/Decorator.cpp +++ b/src/graphics/render/Decorator.cpp @@ -24,6 +24,7 @@ #include "voxels/Chunks.hpp" #include "window/Camera.hpp" #include "world/Level.hpp" +#include "world/Weather.hpp" #include "WorldRenderer.hpp" namespace fs = std::filesystem; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 353c5fabd..01168e602 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -51,6 +51,7 @@ #include "window/Window.hpp" #include "world/Level.hpp" #include "world/LevelEvents.hpp" +#include "world/Weather.hpp" #include "world/World.hpp" #include "debug/Logger.hpp" @@ -84,6 +85,7 @@ WorldRenderer::WorldRenderer( level(frontend.getLevel()), player(player), assets(*engine.getAssets()), + weather(frontend.getWeather()), frustumCulling(std::make_unique()), lineBatch(std::make_unique()), batch3d(std::make_unique(BATCH3D_CAPACITY)), @@ -590,7 +592,7 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) { batch3d->flush(); } -void WorldRenderer::clear() { +void WorldRenderer::resetCache() { chunksRenderer->clear(); } diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index d56c21cbf..49add0351 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -4,7 +4,6 @@ #include "typedefs.hpp" #include "presets/WeatherPreset.hpp" -#include "world/Weather.hpp" #include "window/Camera.hpp" #include "util/ObjectsKeeper.hpp" @@ -36,6 +35,7 @@ class Skybox; class TextsRenderer; class CloudsRenderer; struct EngineSettings; +struct Weather; class WorldRenderer final : public util::ObjectsKeeper { public: @@ -54,7 +54,7 @@ public: PostProcessing& postProcessing ); - void clear(); + void resetCache(); void setDebug(bool flag); @@ -66,6 +66,7 @@ private: const Level& level; Player& player; const Assets& assets; + Weather& weather; std::unique_ptr frustumCulling; std::unique_ptr lineBatch; std::unique_ptr batch3d; @@ -77,13 +78,11 @@ private: std::unique_ptr debugLines; std::unique_ptr precipitation; std::unique_ptr cloudsRenderer; - Weather weather {}; float timer = 0.0f; bool debug = false; bool lightsDebug = false; bool gbufferPipeline = false; - bool dirtySettings = false; /// @brief Render block selection lines diff --git a/src/logic/scripting/lua/libs/libweather.cpp b/src/logic/scripting/lua/libs/libweather.cpp index 76a94a68b..1ae25156b 100644 --- a/src/logic/scripting/lua/libs/libweather.cpp +++ b/src/logic/scripting/lua/libs/libweather.cpp @@ -2,6 +2,7 @@ #include "world/Level.hpp" #include "world/World.hpp" +#include "world/Weather.hpp" using namespace scripting; @@ -9,6 +10,11 @@ static Weather& require_weather() { if (level == nullptr) { throw std::runtime_error("world is not open"); } + if (renderer == nullptr) { + throw std::runtime_error( + "world frontend is not initialized yet (wait for on_hud_open event)" + ); + } return renderer->getWeather(); } From c73b0360998382cb4ab300a6bf8cc69b782438e8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 20 Jun 2026 22:17:27 +0300 Subject: [PATCH 06/52] cleanup World references --- src/engine/ServerMainloop.cpp | 2 +- src/frontend/debug_panel.cpp | 4 ++-- src/frontend/screens/LevelScreen.cpp | 6 +++--- src/graphics/core/Shadows.cpp | 6 +++--- src/graphics/render/WorldRenderer.cpp | 10 ++++------ src/items/Inventories.cpp | 4 ++-- src/logic/ChunksController.cpp | 2 +- src/logic/EngineController.cpp | 12 ++++++------ src/logic/EngineController.hpp | 2 +- src/logic/LevelController.cpp | 10 +++++----- src/logic/scripting/lua/libs/libworld.cpp | 6 +++--- src/logic/scripting/scripting.cpp | 2 +- src/objects/Players.cpp | 4 ++-- src/voxels/GlobalChunks.cpp | 4 ++-- src/world/Level.cpp | 8 ++++---- src/world/Level.hpp | 4 ++-- src/world/World.cpp | 2 +- src/world/World.hpp | 2 ++ 18 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/engine/ServerMainloop.cpp b/src/engine/ServerMainloop.cpp index 54698194e..0c91598b5 100644 --- a/src/engine/ServerMainloop.cpp +++ b/src/engine/ServerMainloop.cpp @@ -57,7 +57,7 @@ void ServerMainloop::run() { } process->update(); if (controller) { - controller->getLevel()->getWorld()->updateTimers(delta); + controller->getLevel()->getWorld().updateTimers(delta); controller->update(glm::min(delta, 0.2), false); } engine.applicationTick(); diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index ba0cc2205..84e85f3e9 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -254,7 +254,7 @@ std::shared_ptr create_debug_panel( } })); panel->add(create_label(gui, [&]() { - return L"seed: " + std::to_wstring(level.getWorld()->getSeed()); + return L"seed: " + std::to_wstring(level.getWorld().getSeed()); })); for (int ax = 0; ax < 3; ax++) { @@ -294,7 +294,7 @@ std::shared_ptr create_debug_panel( sub->add(box, glm::vec2(20, 0)); panel->add(sub); } - auto& worldInfo = level.getWorld()->getInfo(); + auto& worldInfo = level.getWorld().getInfo(); panel->add(create_label(gui, [&]() { int hour, minute, second; timeutil::from_value(worldInfo.daytime, hour, minute, second); diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 613384b7b..24ee44378 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -43,7 +43,7 @@ LevelScreen::LevelScreen( Engine& engine, std::unique_ptr levelPtr, int64_t localPlayer ) : Screen(engine), - world(*levelPtr->getWorld()), + world(levelPtr->getWorld()), postProcessing(std::make_unique( levelPtr->content.getIndices(ResourceType::POST_EFFECT_SLOT).size() )), @@ -98,7 +98,7 @@ LevelScreen::LevelScreen( return false; })); controller->preQuitCallbacks.listen([this]() { - if (!controller->getLevel()->getWorld()->isNameless()) { + if (!controller->getLevel()->getWorld().isNameless()) { saveWorldPreview(); } }); @@ -109,7 +109,7 @@ LevelScreen::LevelScreen( } LevelScreen::~LevelScreen() { - if (!controller->getLevel()->getWorld()->isNameless()) { + if (!controller->getLevel()->getWorld().isNameless()) { saveDecorations(); } scripting::on_frontend_close(); diff --git a/src/graphics/core/Shadows.cpp b/src/graphics/core/Shadows.cpp index 593e4cb4e..8defa40f8 100644 --- a/src/graphics/core/Shadows.cpp +++ b/src/graphics/core/Shadows.cpp @@ -99,7 +99,7 @@ void Shadows::setup(Shader& shader, const Weather& weather) { if (!shadows) { return; } - const auto& worldInfo = level.getWorld()->getInfo(); + const auto& worldInfo = level.getWorld().getInfo(); float cloudsIntensity = weather.clouds(); float shadowsOpacity = 1.0f - cloudsIntensity; shadowsOpacity *= glm::sqrt(glm::abs( @@ -158,8 +158,8 @@ void Shadows::generateShadowsMap( float scale, const std::function& renderShadowPass ) { - auto world = level.getWorld(); - const auto& worldInfo = world->getInfo(); + const auto& world = level.getWorld(); + const auto& worldInfo = world.getInfo(); int resolution = shadowMap.getResolution(); float shadowMapScale = 0.32f / (1 << glm::max(0, quality)) * scale; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 01168e602..965f3fcbf 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -201,7 +201,7 @@ void WorldRenderer::setupWorldShader( shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get()); shader.uniform1i("u_debugLights", lightsDebug); shader.uniform1i("u_debugNormals", false); - shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime); + shader.uniform1f("u_dayTime", level.getWorld().getInfo().daytime); shader.uniform2f("u_lightDir", skybox->getLightDir()); shader.uniform1i("u_skybox", advanced_pipeline::TARGET_SKYBOX); @@ -341,7 +341,7 @@ void WorldRenderer::renderFrameClassic( bool hudVisible, PostProcessing& postProcessing ) { - const auto& worldInfo = level.getWorld()->getInfo(); + const auto& worldInfo = level.getWorld().getInfo(); DrawContext ctx = pctx.sub(); ctx.setDepthTest(true); @@ -381,7 +381,7 @@ void WorldRenderer::renderFrameAdvanced( PostProcessing& postProcessing ) { const auto& settings = engine.getSettings(); - const auto& worldInfo = level.getWorld()->getInfo(); + const auto& worldInfo = level.getWorld().getInfo(); float fogFactor = calcFogFactor(); shadowMapping->refresh(camera, pctx, [this, &camera](Camera& shadowCamera) { @@ -401,8 +401,6 @@ void WorldRenderer::renderFrameAdvanced( ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr); postProcessing.bindDepthBuffer(); - - // Background sky plane skybox->draw( level.environment, ctx, camera, worldInfo.daytime, weather.clouds() ); @@ -441,7 +439,7 @@ void WorldRenderer::renderFrame( dirtySettings = false; } - const auto& worldInfo = level.getWorld()->getInfo(); + const auto& worldInfo = level.getWorld().getInfo(); float clouds = weather.clouds(); float random = rand() / static_cast(RAND_MAX); diff --git a/src/items/Inventories.cpp b/src/items/Inventories.cpp index 249f5d66d..424bcf2c1 100644 --- a/src/items/Inventories.cpp +++ b/src/items/Inventories.cpp @@ -12,7 +12,7 @@ Inventories::Inventories(Level& level) : level(level) { Inventories::~Inventories() = default; std::shared_ptr Inventories::create(size_t size) { - int64_t id = level.getWorld()->getNextInventoryId(); + int64_t id = level.getWorld().getNextInventoryId(); auto inv = std::make_shared(id, size); store(inv); return inv; @@ -52,7 +52,7 @@ std::shared_ptr Inventories::clone(int64_t id) { auto original = get(id); if (original == nullptr) return nullptr; auto clone = std::make_shared(*original); - clone->setId(level.getWorld()->getNextInventoryId()); + clone->setId(level.getWorld().getNextInventoryId()); store(clone); return clone; } diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 1dc791b19..368d641d6 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -27,7 +27,7 @@ ChunksController::ChunksController(Level& level) generator(std::make_unique( level.content.generators.require(level.environment.generator), level.content, - level.getWorld()->getSeed() + level.getWorld().getSeed() )) {} ChunksController::~ChunksController() = default; diff --git a/src/logic/EngineController.cpp b/src/logic/EngineController.cpp index ec9ef0b2a..51b32e0c4 100644 --- a/src/logic/EngineController.cpp +++ b/src/logic/EngineController.cpp @@ -283,7 +283,7 @@ void EngineController::createWorld( // generator-based environments must die in 1.0 bool genBasedEnv = false; - if (auto genDef = generators.find(environment)) { + if (generators.find(environment)) { genBasedEnv = true; } @@ -314,8 +314,8 @@ void EngineController::setLocalPlayer(int64_t player) { localPlayer = player; } -void EngineController::reopenWorld(World* world) { - std::string name = world->wfile->getFolder().name(); +void EngineController::reopenWorld(World& world) { + std::string name = world.wfile->getFolder().name(); engine.onWorldClosed(); openWorld(name, true); } @@ -384,16 +384,16 @@ void EngineController::reconfigPacks( } } else { auto level = controller->getLevel(); - auto world = level->getWorld(); + auto& world = level->getWorld(); controller->processBeforeQuit(); controller->saveWorld(); - auto names = PacksManager::getNames(world->getPacks()); + auto names = PacksManager::getNames(world.getPacks()); auto& manager = contentControl.scan(); reconfig_packs_inside(manager, names, packsToAdd, packsToRemove); const auto& settings = engine.getSettings(); - auto& wfile = *world->wfile; + auto& wfile = *world.wfile; if (!settings.debug.generatorTestMode.get()) { wfile.removeIndices(packsToRemove); } diff --git a/src/logic/EngineController.hpp b/src/logic/EngineController.hpp index baba7ba9e..a40f3476f 100644 --- a/src/logic/EngineController.hpp +++ b/src/logic/EngineController.hpp @@ -41,5 +41,5 @@ public: void setLocalPlayer(int64_t player); - void reopenWorld(World* world); + void reopenWorld(World& world); }; diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 1c40b20df..62a10c219 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -141,16 +141,16 @@ void LevelController::processBeforeQuit() { } void LevelController::saveWorld() { - auto world = level->getWorld(); - if (world->isNameless()) { + auto& world = level->getWorld(); + if (world.isNameless()) { logger.info() << "nameless world will not be saved"; return; } - logger.info() << "writing world '" << world->getName() << "'"; - world->wfile->createDirectories(); + logger.info() << "writing world '" << world.getName() << "'"; + world.wfile->createDirectories(); scripting::on_world_save(); level->onSave(); - level->getWorld()->write(*level); + level->getWorld().write(*level); } void LevelController::onWorldQuit() { diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 9c50b6c0b..a9105dfe2 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -33,7 +33,7 @@ static Level& require_level() { } static WorldInfo& require_world_info() { - return require_level().getWorld()->getInfo(); + return require_level().getWorld().getInfo(); } static int l_is_open(lua::State* L) { @@ -136,7 +136,7 @@ static int l_get_chunk_data(lua::State* L) { auto voxelData = std::make_unique(CHUNK_DATA_LEN); std::vector chunkData; if (chunk == nullptr) { - auto& regions = level->getWorld()->wfile->getRegions(); + auto& regions = level->getWorld().wfile->getRegions(); if (!regions.getVoxels(x, z, voxelData.get())) { return 0; } @@ -215,7 +215,7 @@ static int l_save_chunk_data(lua::State* L) { reinterpret_cast(buffer.data()), reinterpret_cast(buffer.data()) + buffer.size() ), - level->getWorld()->wfile->getRegions() + level->getWorld().wfile->getRegions() ); return 0; } diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 37597b058..65caa8877 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -310,7 +310,7 @@ void scripting::on_world_load(LevelController* controller) { for (auto& pack : content_control->getAllContentPacks()) { lua::emit_event(L, pack.id + ":.worldopen", [](auto L) { return lua::pushboolean( - L, !scripting::level->getWorld()->getInfo().isLoaded + L, !scripting::level->getWorld().getInfo().isLoaded ); }); } diff --git a/src/objects/Players.cpp b/src/objects/Players.cpp index c100e3d67..04c1aa2aa 100644 --- a/src/objects/Players.cpp +++ b/src/objects/Players.cpp @@ -67,7 +67,7 @@ Player* Players::getNearest(const glm::vec3& position) const { } Player* Players::create(int64_t id) { - int64_t& nextPlayerID = level.getWorld()->getInfo().nextPlayerId; + int64_t& nextPlayerID = level.getWorld().getInfo().nextPlayerId; if (id == NONE) { id = nextPlayerID++; } else { @@ -146,7 +146,7 @@ void Players::deserialize(const dv::value& src) { auto& inventory = player->getInventory(); // invalid inventory id pre 0.25 if (inventory->getId() == 0) { - inventory->setId(level.getWorld()->getNextInventoryId()); + inventory->setId(level.getWorld().getNextInventoryId()); } level.inventories->store(inventory); } diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index d24d8b26d..c081a6a5b 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -107,7 +107,7 @@ std::shared_ptr GlobalChunks::create(int x, int z, bool lighting) { chunks_pool.create(x, z, lighting ? lightmaps_pool.create() : nullptr); chunksMap[keyfrom(x, z)] = chunk; - World& world = *level.getWorld(); + World& world = level.getWorld(); auto& regions = world.wfile.get()->getRegions(); if (regions.getVoxels(chunk->x, chunk->z, voxelDataBuffer.get())) { @@ -197,7 +197,7 @@ void GlobalChunks::save(Chunk* chunk) { if (!entities.empty()) { chunk->flags.entities = true; } - level.getWorld()->wfile->getRegions().put( + level.getWorld().wfile->getRegions().put( chunk, chunk->flags.entities ? json::to_binary(root, true) : std::vector() diff --git a/src/world/Level.cpp b/src/world/Level.cpp index 9e40e90cd..308604ca9 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -71,12 +71,12 @@ Level::Level( Level::~Level() = default; -World* Level::getWorld() { - return world.get(); +World& Level::getWorld() { + return *world; } -const World* Level::getWorld() const { - return world.get(); +const World& Level::getWorld() const { + return *world; } void Level::onSave() { diff --git a/src/world/Level.hpp b/src/world/Level.hpp index c8945a23c..bf48c35cf 100644 --- a/src/world/Level.hpp +++ b/src/world/Level.hpp @@ -46,9 +46,9 @@ public: ); ~Level(); - World* getWorld(); + World& getWorld(); - const World* getWorld() const; + const World& getWorld() const; void onSave(); diff --git a/src/world/World.cpp b/src/world/World.cpp index 8bbc78b94..d65dd2636 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -147,7 +147,7 @@ std::unique_ptr World::load( if (!playerRoot["players"].empty()) { if (!playerRoot["players"][0].has("id")) { - level->getWorld()->getInfo().nextPlayerId++; + level->getWorld().getInfo().nextPlayerId++; } } } diff --git a/src/world/World.hpp b/src/world/World.hpp index 7af7bd1db..dc9439906 100644 --- a/src/world/World.hpp +++ b/src/world/World.hpp @@ -67,6 +67,8 @@ public: const std::vector& packs ); + World(const World&) = delete; + ~World(); /// @brief Update world day-time and total time From 30ab6c2f5e147869cc91741b9a12d7f3006d166d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 6 Jul 2026 01:05:44 +0300 Subject: [PATCH 07/52] remove unnecessary drawGroup-based loops --- src/graphics/render/BlocksRenderer.cpp | 336 ++++++++++++------------- src/graphics/render/BlocksRenderer.hpp | 12 +- src/graphics/render/ChunksRenderer.cpp | 7 +- src/voxels/VoxelsVolume.hpp | 20 +- 4 files changed, 180 insertions(+), 195 deletions(-) diff --git a/src/graphics/render/BlocksRenderer.cpp b/src/graphics/render/BlocksRenderer.cpp index 0aae42010..6ccc5dcf6 100644 --- a/src/graphics/render/BlocksRenderer.cpp +++ b/src/graphics/render/BlocksRenderer.cpp @@ -10,15 +10,14 @@ #include "frontend/ContentGfxCache.hpp" const glm::vec3 BlocksRenderer::SUN_VECTOR(0.528265, 0.833149, -0.163704); -const float DIRECTIONAL_LIGHT_FACTOR = 0.3f; +constexpr float DIRECTIONAL_LIGHT_FACTOR = 0.3f; BlocksRenderer::BlocksRenderer( size_t capacity, - const Content& content, + const Block* const* blockDefs, const ContentGfxCache& cache, const EngineSettings& settings -) : content(content), - vertexBuffer(std::make_unique(capacity)), +) : vertexBuffer(std::make_unique(capacity)), indexBuffer(std::make_unique(capacity)), denseIndexBuffer(std::make_unique(capacity)), vertexCount(0), @@ -28,7 +27,7 @@ BlocksRenderer::BlocksRenderer( cache(cache), settings(settings) { - blockDefsCache = content.getIndices()->blocks.getDefs(); + blockDefsCache = blockDefs; } BlocksRenderer::~BlocksRenderer() = default; @@ -482,84 +481,78 @@ glm::vec4 BlocksRenderer::pickSoftLight( } void BlocksRenderer::render( - const voxel* voxels, const int beginEnds[256][2] + const voxel* voxels, int totalBegin, int totalEnd ) { bool denseRender = this->denseRender; bool densePass = this->densePass; bool enableAO = settings.graphics.softLighting.get(); - for (const auto drawGroup : *content.drawGroups) { - int begin = beginEnds[drawGroup][0]; - if (begin == 0) { + + for (int i = totalBegin; i < totalEnd; i++) { + const voxel& vox = voxels[i]; + blockid_t id = vox.id; + blockstate state = vox.state; + const auto& def = *blockDefsCache[id]; + uint8_t variantId = def.getVariantIndex(state.userbits); + const auto& variant = def.getVariant(variantId); + if (id == 0 || state.segment) { continue; } - int end = beginEnds[drawGroup][1]; - for (int i = begin-1; i <= end; i++) { - const voxel& vox = voxels[i]; - blockid_t id = vox.id; - blockstate state = vox.state; - const auto& def = *blockDefsCache[id]; - uint8_t variantId = def.getVariantIndex(state.userbits); - const auto& variant = def.getVariant(variantId); - if (id == 0 || variant.drawGroup != drawGroup || state.segment) { - continue; + if (denseRender != (variant.culling == CullingMode::OPTIONAL)) { + continue; + } + if (def.translucent) { + continue; + } + const UVRegion texfaces[6] { + cache.getRegion(id, variantId, 0, densePass), + cache.getRegion(id, variantId, 1, densePass), + cache.getRegion(id, variantId, 2, densePass), + cache.getRegion(id, variantId, 3, densePass), + cache.getRegion(id, variantId, 4, densePass), + cache.getRegion(id, variantId, 5, densePass) + }; + int x = i % CHUNK_W; + int y = i / (CHUNK_D * CHUNK_W); + int z = (i / CHUNK_D) % CHUNK_W; + switch (def.getModel(state.userbits).type) { + case BlockModelType::BLOCK: + blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless, + def.ambientOcclusion && enableAO); + break; + case BlockModelType::XSPRITE: { + if (!denseRender) + blockXSprite(x, y, z, glm::vec3(1.0f), + texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f); + break; } - if (denseRender != (variant.culling == CullingMode::OPTIONAL)) { - continue; + case BlockModelType::AABB: { + if (!denseRender) + blockAABB({x, y, z}, texfaces, &def, vox.state.rotation, + !def.shadeless, def.ambientOcclusion && enableAO); + break; } - if (def.translucent) { - continue; - } - const UVRegion texfaces[6] { - cache.getRegion(id, variantId, 0, densePass), - cache.getRegion(id, variantId, 1, densePass), - cache.getRegion(id, variantId, 2, densePass), - cache.getRegion(id, variantId, 3, densePass), - cache.getRegion(id, variantId, 4, densePass), - cache.getRegion(id, variantId, 5, densePass) - }; - int x = i % CHUNK_W; - int y = i / (CHUNK_D * CHUNK_W); - int z = (i / CHUNK_D) % CHUNK_W; - switch (def.getModel(state.userbits).type) { - case BlockModelType::BLOCK: - blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless, - def.ambientOcclusion && enableAO); - break; - case BlockModelType::XSPRITE: { - if (!denseRender) - blockXSprite(x, y, z, glm::vec3(1.0f), - texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f); - break; - } - case BlockModelType::AABB: { - if (!denseRender) - blockAABB({x, y, z}, texfaces, &def, vox.state.rotation, - !def.shadeless, def.ambientOcclusion && enableAO); - break; - } - case BlockModelType::CUSTOM: { - if (!denseRender) - blockCustomModel( - {x, y, z}, - def, - vox.state, - !def.shadeless, - def.ambientOcclusion && enableAO - ); - break; - } - default: - break; - } - if (overflow) { - return; + case BlockModelType::CUSTOM: { + if (!denseRender) + blockCustomModel( + {x, y, z}, + def, + vox.state, + !def.shadeless, + def.ambientOcclusion && enableAO + ); + break; } + default: + break; + } + if (overflow) { + return; } } } SortingMeshData BlocksRenderer::renderTranslucent( - const voxel* voxels, int beginEnds[256][2] + const voxel* voxels, int totalBegin, int totalEnd ) { SortingMeshData sortingMesh {{}}; @@ -569,106 +562,98 @@ SortingMeshData BlocksRenderer::renderTranslucent( bool densePass = this->densePass; bool enableAO = settings.graphics.softLighting.get(); - for (const auto drawGroup : *content.drawGroups) { - int begin = beginEnds[drawGroup][0]; - if (begin == 0) { + for (int i = totalBegin; i < totalEnd; i++) { + const voxel& vox = voxels[i]; + blockid_t id = vox.id; + blockstate state = vox.state; + const auto& def = *blockDefsCache[id]; + uint8_t variantId = def.getVariantIndex(state.userbits); + if (id == 0 || state.segment) { continue; } - int end = beginEnds[drawGroup][1]; - for (int i = begin-1; i <= end; i++) { - const voxel& vox = voxels[i]; - blockid_t id = vox.id; - blockstate state = vox.state; - const auto& def = *blockDefsCache[id]; - uint8_t variantId = def.getVariantIndex(state.userbits); - const auto& variant = def.getVariant(variantId); - if (id == 0 || variant.drawGroup != drawGroup || state.segment) { - continue; - } - if (!def.translucent) { - continue; - } - const UVRegion texfaces[6] { - cache.getRegion(id, variantId, 0, densePass), - cache.getRegion(id, variantId, 1, densePass), - cache.getRegion(id, variantId, 2, densePass), - cache.getRegion(id, variantId, 3, densePass), - cache.getRegion(id, variantId, 4, densePass), - cache.getRegion(id, variantId, 5, densePass) - }; - int x = i % CHUNK_W; - int y = i / (CHUNK_D * CHUNK_W); - int z = (i / CHUNK_D) % CHUNK_W; - switch (def.getModel(state.userbits).type) { - case BlockModelType::BLOCK: - blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless, - def.ambientOcclusion && enableAO); - break; - case BlockModelType::XSPRITE: { - blockXSprite(x, y, z, glm::vec3(1.0f), - texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f); - break; - } - case BlockModelType::AABB: { - blockAABB( - {x, y, z}, - texfaces, - &def, - vox.state.rotation, - !def.shadeless, - def.ambientOcclusion && enableAO - ); - break; - } - case BlockModelType::CUSTOM: { - blockCustomModel( - {x, y, z}, - def, - vox.state, - !def.shadeless, - def.ambientOcclusion && enableAO - ); - break; - } - default: - break; - } - if (vertexCount == 0) { - continue; - } - SortingMeshEntry entry { - glm::vec3( - x + chunk->x * CHUNK_W + 0.5f, - y + 0.5f, - z + chunk->z * CHUNK_D + 0.5f - ), - util::Buffer(indexCount), 0}; - - totalSize += entry.vertexData.size(); - - for (int j = 0; j < indexCount; j++) { - std::memcpy( - entry.vertexData.data() + j, - vertexBuffer.get() + indexBuffer[j], - sizeof(ChunkVertex) - ); - ChunkVertex& vertex = entry.vertexData[j]; - - if (!aabbInit) { - aabbInit = true; - aabb.a = aabb.b = vertex.position; - } else { - aabb.addPoint(vertex.position); - } - - vertex.position.x += chunk->x * CHUNK_W + 0.5f; - vertex.position.y += 0.5f; - vertex.position.z += chunk->z * CHUNK_D + 0.5f; - } - sortingMesh.entries.push_back(std::move(entry)); - vertexCount = 0; - vertexOffset = indexCount = 0; + if (!def.translucent) { + continue; } + const UVRegion texfaces[6] { + cache.getRegion(id, variantId, 0, densePass), + cache.getRegion(id, variantId, 1, densePass), + cache.getRegion(id, variantId, 2, densePass), + cache.getRegion(id, variantId, 3, densePass), + cache.getRegion(id, variantId, 4, densePass), + cache.getRegion(id, variantId, 5, densePass) + }; + int x = i % CHUNK_W; + int y = i / (CHUNK_D * CHUNK_W); + int z = (i / CHUNK_D) % CHUNK_W; + switch (def.getModel(state.userbits).type) { + case BlockModelType::BLOCK: + blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless, + def.ambientOcclusion && enableAO); + break; + case BlockModelType::XSPRITE: { + blockXSprite(x, y, z, glm::vec3(1.0f), + texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f); + break; + } + case BlockModelType::AABB: { + blockAABB( + {x, y, z}, + texfaces, + &def, + vox.state.rotation, + !def.shadeless, + def.ambientOcclusion && enableAO + ); + break; + } + case BlockModelType::CUSTOM: { + blockCustomModel( + {x, y, z}, + def, + vox.state, + !def.shadeless, + def.ambientOcclusion && enableAO + ); + break; + } + default: + break; + } + if (vertexCount == 0) { + continue; + } + SortingMeshEntry entry { + glm::vec3( + x + chunk->x * CHUNK_W + 0.5f, + y + 0.5f, + z + chunk->z * CHUNK_D + 0.5f + ), + util::Buffer(indexCount), 0}; + + totalSize += entry.vertexData.size(); + + for (int j = 0; j < indexCount; j++) { + std::memcpy( + entry.vertexData.data() + j, + vertexBuffer.get() + indexBuffer[j], + sizeof(ChunkVertex) + ); + ChunkVertex& vertex = entry.vertexData[j]; + + if (!aabbInit) { + aabbInit = true; + aabb.a = aabb.b = vertex.position; + } else { + aabb.addPoint(vertex.position); + } + + vertex.position.x += chunk->x * CHUNK_W + 0.5f; + vertex.position.y += 0.5f; + vertex.position.z += chunk->z * CHUNK_D + 0.5f; + } + sortingMesh.entries.push_back(std::move(entry)); + vertexCount = 0; + vertexOffset = indexCount = 0; } // additional powerful optimization @@ -710,19 +695,16 @@ void BlocksRenderer::build( int totalBegin = chunk->bottom * (CHUNK_W * CHUNK_D); int totalEnd = chunk->top * (CHUNK_W * CHUNK_D); + bool hasTranslucent = false; - int beginEnds[256][2] {}; for (int i = totalBegin; i < totalEnd; i++) { const voxel& vox = voxels[i]; blockid_t id = vox.id; const auto& def = *blockDefsCache[id]; - const auto& variant = def.getVariantByBits(vox.state.userbits); - hasTranslucent = def.translucent || hasTranslucent; - - if (beginEnds[variant.drawGroup][0] == 0) { - beginEnds[variant.drawGroup][0] = i + 1; + if (def.translucent) { + hasTranslucent = true; + break; } - beginEnds[variant.drawGroup][1] = i; } cancelled = false; @@ -734,7 +716,7 @@ void BlocksRenderer::build( densePass = false; if (hasTranslucent) { - sortingMesh = renderTranslucent(voxels, beginEnds); + sortingMesh = renderTranslucent(voxels, totalBegin, totalEnd); } else { sortingMesh = {}; } @@ -747,13 +729,13 @@ void BlocksRenderer::build( denseRender = false; densePass = false; - render(voxels, beginEnds); + render(voxels, totalBegin, totalEnd); size_t endIndex = indexCount; denseRender = true; densePass = settings.graphics.denseRender.get(); - render(voxels, beginEnds); + render(voxels, totalBegin, totalEnd); denseIndexCount = indexCount; for (size_t i = 0; i < denseIndexCount; i++) { @@ -762,7 +744,7 @@ void BlocksRenderer::build( indexCount = endIndex; densePass = false; - render(voxels, beginEnds); + render(voxels, totalBegin, totalEnd); } ChunkMeshData BlocksRenderer::createMesh() { diff --git a/src/graphics/render/BlocksRenderer.hpp b/src/graphics/render/BlocksRenderer.hpp index 94b708a54..0488f0080 100644 --- a/src/graphics/render/BlocksRenderer.hpp +++ b/src/graphics/render/BlocksRenderer.hpp @@ -25,7 +25,7 @@ class BlocksRenderer final { public: BlocksRenderer( size_t capacity, - const Content& content, + const Block* const* blockDefs, const ContentGfxCache& cache, const EngineSettings& settings ); @@ -44,7 +44,6 @@ public: } private: static const glm::vec3 SUN_VECTOR; - const Content& content; std::unique_ptr vertexBuffer; std::unique_ptr indexBuffer; std::unique_ptr denseIndexBuffer; @@ -145,13 +144,16 @@ private: ); // Does block allow to see other blocks sides (is it transparent) - inline bool isOpen(const glm::ivec3& pos, const Block& def, const Variant& variant) const { + bool isOpen(const glm::ivec3& pos, const Block& def, const Variant& variant) const { const auto& vox = voxelsBuffer->pickBlock( chunk->x * CHUNK_W + pos.x, pos.y, chunk->z * CHUNK_D + pos.z ); if (vox.id == BLOCK_VOID) { return false; } + if (def.rt.id == vox.id && def.rt.solid && variant.culling == CullingMode::DEFAULT) { + return false; + } const auto& block = *blockDefsCache[vox.id]; const auto& blockVariant = block.getVariantByBits(vox.state.userbits); uint8_t otherDrawGroup = blockVariant.drawGroup; @@ -178,6 +180,6 @@ private: float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up ) const; - void render(const voxel* voxels, const int beginEnds[256][2]); - SortingMeshData renderTranslucent(const voxel* voxels, int beginEnds[256][2]); + void render(const voxel* voxels, int totalBegin, int totalEnd); + SortingMeshData renderTranslucent(const voxel* voxels, int totalBegin, int totalEnd); }; diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index baf4cf356..7e81e0bd5 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -14,6 +14,7 @@ #include "util/listutil.hpp" #include "util/ObjectsPool.hpp" #include "settings.hpp" +#include "content/Content.hpp" static debug::Logger logger("chunks-render"); @@ -33,7 +34,7 @@ public: settings.graphics.denseRender.get() ? settings.graphics.chunkMaxVerticesDense.get() : settings.graphics.chunkMaxVertices.get(), - level.content, + level.content.getIndices()->blocks.getDefs(), cache, settings ) { @@ -92,7 +93,7 @@ ChunksRenderer::ChunksRenderer( threadPool.setStopOnFail(false); renderer = std::make_unique( settings.graphics.chunkMaxVertices.get(), - level.content, cache, settings + level.content.getIndices()->blocks.getDefs(), cache, settings ); logger.info() << "created " << threadPool.getWorkersCount() << " workers"; logger.info() << "memory consumption is " @@ -126,7 +127,7 @@ const ChunkMesh* ChunksRenderer::render( const std::shared_ptr& chunk, bool important, bool lowPriority ) { glm::ivec2 key(chunk->x, chunk->z); - if (important) { + if (important || true) { ChunkMesh mesh {}; auto voxelsBuffer = prepareVoxelsVolume(*chunk); mesh = renderer->render(chunk.get(), *voxelsBuffer); diff --git a/src/voxels/VoxelsVolume.hpp b/src/voxels/VoxelsVolume.hpp index 471b159c5..d90acfa5d 100644 --- a/src/voxels/VoxelsVolume.hpp +++ b/src/voxels/VoxelsVolume.hpp @@ -66,7 +66,7 @@ public: return lights.get(); } - inline blockid_t pickBlockId(int bx, int by, int bz) const { + blockid_t pickBlockId(int bx, int by, int bz) const { if (bx < x || by < y || bz < z || bx >= x + w || by >= y + h || bz >= z + d) { return BLOCK_VOID; @@ -75,7 +75,7 @@ public: } - inline voxel pickBlock(int bx, int by, int bz) const { + voxel pickBlock(int bx, int by, int bz) const { if (bx < x || by < y || bz < z || bx >= x + w || by >= y + h || bz >= z + d) { return {BLOCK_VOID, {}}; @@ -83,7 +83,7 @@ public: return voxels[vox_index(bx - x, by - y, bz - z, w, d)]; } - inline light_t pickLight(int bx, int by, int bz) const { + light_t pickLight(int bx, int by, int bz) const { if (bx < x || by < y || bz < z || bx >= x + w || by >= y + h || bz >= z + d) { return 0; @@ -103,10 +103,10 @@ private: template class StaticVoxelsVolume { public: - static inline constexpr size_t size = w * h * d; - static inline constexpr int width = w; - static inline constexpr int height = h; - static inline constexpr int depth = d; + static constexpr size_t size = w * h * d; + static constexpr int width = w; + static constexpr int height = h; + static constexpr int depth = d; StaticVoxelsVolume() : StaticVoxelsVolume(0, 0, 0) {} @@ -142,7 +142,7 @@ public: return lights; } - inline blockid_t pickBlockId(uint bx, uint by, uint bz) const { + blockid_t pickBlockId(uint bx, uint by, uint bz) const { bx -= x; by -= y; bz -= z; @@ -153,7 +153,7 @@ public: } - inline const voxel& pickBlock(uint bx, uint by, uint bz) const { + const voxel& pickBlock(uint bx, uint by, uint bz) const { bx -= x; by -= y; bz -= z; @@ -164,7 +164,7 @@ public: return voxels[vox_index(bx, by, bz, w, d)]; } - inline light_t pickLight(uint bx, uint by, uint bz) const { + light_t pickLight(uint bx, uint by, uint bz) const { bx -= x; by -= y; bz -= z; From 5b868ae857b1b425a4f529c663b3dad55fcbf59a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 14 Jul 2026 19:03:32 +0300 Subject: [PATCH 08/52] fix graphics settings & reduce extra GL calls --- src/graphics/core/Batch2D.cpp | 2 +- src/graphics/core/Batch3D.cpp | 4 ++-- src/graphics/core/LineBatch.cpp | 2 +- src/graphics/core/Mesh.hpp | 11 ++++++++--- src/graphics/core/Mesh.inl | 15 ++++++++------- src/graphics/render/MainBatch.cpp | 2 +- src/graphics/render/WorldRenderer.hpp | 2 +- 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/graphics/core/Batch2D.cpp b/src/graphics/core/Batch2D.cpp index 7aa856f3f..33265b49f 100644 --- a/src/graphics/core/Batch2D.cpp +++ b/src/graphics/core/Batch2D.cpp @@ -372,7 +372,7 @@ void Batch2D::sprite( void Batch2D::flush() { if (index == 0) return; - mesh->reload(buffer.get(), index); + mesh->reload(buffer.get(), index, true); mesh->draw(gl::to_glenum(primitive)); index = 0; } diff --git a/src/graphics/core/Batch3D.cpp b/src/graphics/core/Batch3D.cpp index ce9f176c7..3aa996706 100644 --- a/src/graphics/core/Batch3D.cpp +++ b/src/graphics/core/Batch3D.cpp @@ -292,13 +292,13 @@ void Batch3D::point(const glm::vec3& coord, const glm::vec4& tint) { } void Batch3D::flush() { - mesh->reload(buffer.get(), index); + mesh->reload(buffer.get(), index, true); mesh->draw(); index = 0; } void Batch3D::flushPoints() { - mesh->reload(buffer.get(), index); + mesh->reload(buffer.get(), index, true); mesh->draw(GL_POINTS); index = 0; } diff --git a/src/graphics/core/LineBatch.cpp b/src/graphics/core/LineBatch.cpp index 9e33398ac..514f550d6 100644 --- a/src/graphics/core/LineBatch.cpp +++ b/src/graphics/core/LineBatch.cpp @@ -57,7 +57,7 @@ void LineBatch::box(float x, float y, float z, float w, float h, float d, void LineBatch::flush(){ if (index == 0) return; - mesh->reload(buffer.get(), index); + mesh->reload(buffer.get(), index, true); mesh->draw(GL_LINES); index = 0; } diff --git a/src/graphics/core/Mesh.hpp b/src/graphics/core/Mesh.hpp index 48c029696..2ff15cc2a 100644 --- a/src/graphics/core/Mesh.hpp +++ b/src/graphics/core/Mesh.hpp @@ -47,12 +47,15 @@ public: void reload( const VertexStructure* vertexBuffer, size_t vertexCount, - const std::vector& indices + const std::vector& indices, + bool streaming ); - void reload(const VertexStructure* vertexBuffer, size_t vertexCount) { + void reload( + const VertexStructure* vertexBuffer, size_t vertexCount, bool streaming + ) { static const std::vector indices {}; - reload(vertexBuffer, vertexCount, indices); + reload(vertexBuffer, vertexCount, indices, streaming); } /// @brief Draw mesh with specified primitives type @@ -61,6 +64,8 @@ public: /// @brief Draw mesh as triangles void draw() const; +private: + }; #include "graphics/core/Mesh.inl" diff --git a/src/graphics/core/Mesh.inl b/src/graphics/core/Mesh.inl index 913da2bce..4c6424dd1 100644 --- a/src/graphics/core/Mesh.inl +++ b/src/graphics/core/Mesh.inl @@ -46,9 +46,9 @@ Mesh::Mesh( glGenVertexArrays(1, &vao); glGenBuffers(1, &vbo); - reload(vertexBuffer, vertices, std::move(indices)); - glBindVertexArray(vao); + reload(vertexBuffer, vertices, std::move(indices), false); + // attributes int offset = 0; for (int i = 0; attrs[i].count; i++) { @@ -82,20 +82,22 @@ template void Mesh::reload( const VertexStructure* vertexBuffer, size_t vertexCount, - const std::vector& indices + const std::vector& indices, + bool streaming ) { this->vertexCount = vertexCount; - glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vbo); if (vertexBuffer != nullptr && vertexCount != 0) { glBufferData( GL_ARRAY_BUFFER, vertexCount * sizeof(VertexStructure), vertexBuffer, - GL_STREAM_DRAW + streaming ? GL_STREAM_DRAW : GL_STATIC_DRAW ); } else { - glBufferData(GL_ARRAY_BUFFER, 0, {}, GL_STREAM_DRAW); + glBufferData( + GL_ARRAY_BUFFER, 0, {}, streaming ? GL_STREAM_DRAW : GL_STATIC_DRAW + ); } for (int i = 0; i < ibos.size(); i++) { @@ -116,7 +118,6 @@ void Mesh::reload( GL_STATIC_DRAW ); } - glBindVertexArray(0); } template diff --git a/src/graphics/render/MainBatch.cpp b/src/graphics/render/MainBatch.cpp index 1a501ca15..91364e510 100644 --- a/src/graphics/render/MainBatch.cpp +++ b/src/graphics/render/MainBatch.cpp @@ -46,7 +46,7 @@ void MainBatch::flush() { texture = blank.get(); } texture->bind(); - mesh->reload(buffer.get(), index); + mesh->reload(buffer.get(), index, true); mesh->draw(); index = 0; } diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index 49add0351..b3b10f88c 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -83,7 +83,7 @@ private: bool debug = false; bool lightsDebug = false; bool gbufferPipeline = false; - bool dirtySettings = false; + bool dirtySettings = true; /// @brief Render block selection lines void renderBlockSelection(); From 17f3cbce954fe14f0a789f444a5db09f927e068d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 14 Jul 2026 20:12:56 +0300 Subject: [PATCH 09/52] update Device.hpp --- src/io/devices/Device.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/devices/Device.hpp b/src/io/devices/Device.hpp index cfbe4b1e0..4cad4c5de 100644 --- a/src/io/devices/Device.hpp +++ b/src/io/devices/Device.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include "../path.hpp" From 413004d7311f382373a8983fc6783159de4cb438 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 14 Jul 2026 23:59:09 +0300 Subject: [PATCH 10/52] test dithering solution --- res/shaders/entity.glslf | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/res/shaders/entity.glslf b/res/shaders/entity.glslf index f3293adb7..d2eb78f4d 100644 --- a/res/shaders/entity.glslf +++ b/res/shaders/entity.glslf @@ -15,11 +15,32 @@ uniform float u_fogCurve; uniform bool u_alphaClip; uniform vec3 u_sunDir; +const float BAYER_MATRIX[64] = float[]( + 0.0, 32.0, 8.0, 40.0, 2.0, 34.0, 10.0, 42.0, + 48.0, 16.0, 56.0, 24.0, 50.0, 18.0, 58.0, 26.0, + 12.0, 44.0, 4.0, 36.0, 14.0, 46.0, 6.0, 38.0, + 60.0, 28.0, 52.0, 20.0, 62.0, 30.0, 54.0, 22.0, + 3.0, 35.0, 11.0, 43.0, 1.0, 33.0, 9.0, 41.0, + 51.0, 19.0, 59.0, 27.0, 49.0, 17.0, 57.0, 25.0, + 15.0, 47.0, 7.0, 39.0, 13.0, 45.0, 5.0, 37.0, + 63.0, 31.0, 55.0, 23.0, 61.0, 29.0, 53.0, 21.0 +); + +float calc_bayer(vec2 pos){ + ivec2 p = ivec2(mod(pos, 8.0)); + return BAYER_MATRIX[p.y * 8 + p.x] / 64.0; +} + void main() { vec4 texColor = texture(u_texture0, a_texCoord); float alpha = a_color.a * texColor.a; // anyway it's any alpha-test alternative required - if (alpha < (u_alphaClip ? 0.5f : 0.15f)) { + if (u_alphaClip) { + float bayer = calc_bayer(gl_FragCoord.xy); + if (alpha * bayer * bayer < 0.1f) { + discard; + } + } else if (alpha < 0.05f) { discard; } f_color = a_color * texColor; From 08fae1a47c343615fe12fb80cc256a9f81ee94fe Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 15 Jul 2026 00:38:54 +0300 Subject: [PATCH 11/52] fix --- res/shaders/entity.glslf | 1 + 1 file changed, 1 insertion(+) diff --git a/res/shaders/entity.glslf b/res/shaders/entity.glslf index d2eb78f4d..5620ef20a 100644 --- a/res/shaders/entity.glslf +++ b/res/shaders/entity.glslf @@ -40,6 +40,7 @@ void main() { if (alpha * bayer * bayer < 0.1f) { discard; } + alpha = 1.0; } else if (alpha < 0.05f) { discard; } From db4f05cf36ade3ddad63cb662bef151d8950cbf9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 15 Jul 2026 00:47:15 +0300 Subject: [PATCH 12/52] update entity.glslf --- res/shaders/entity.glslf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/shaders/entity.glslf b/res/shaders/entity.glslf index 5620ef20a..a817d963a 100644 --- a/res/shaders/entity.glslf +++ b/res/shaders/entity.glslf @@ -37,7 +37,7 @@ void main() { // anyway it's any alpha-test alternative required if (u_alphaClip) { float bayer = calc_bayer(gl_FragCoord.xy); - if (alpha * bayer * bayer < 0.1f) { + if (alpha - bayer < 0.01f) { discard; } alpha = 1.0; From a6c4e9e22e5696a09aec48d46064c260fa00938c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 15 Jul 2026 19:28:02 +0300 Subject: [PATCH 13/52] reduce fog factor for emissive geometry --- res/shaders/effects/deferred_lighting.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/shaders/effects/deferred_lighting.glsl b/res/shaders/effects/deferred_lighting.glsl index c314df02a..6207f1583 100644 --- a/res/shaders/effects/deferred_lighting.glsl +++ b/res/shaders/effects/deferred_lighting.glsl @@ -32,6 +32,6 @@ vec4 effect() { light = pow(light, u_gamma); vec3 fogColor = texture(u_skybox, dir).rgb; - float fog = calc_fog(length(u_view * vec4((modelpos.xyz - u_cameraPos) * FOG_POS_SCALE, 0.0)) / 256.0); + float fog = calc_fog(pow(length(u_view * vec4((modelpos.xyz - u_cameraPos) * FOG_POS_SCALE, 0.0)) / 256.0, (1.0f - emission * 0.7))); return vec4(mix(texture(u_screen, v_uv).rgb * mix(1.0, light, 1.0), fogColor, fog), 1.0); } From 86096553ce847768cc1b17ebf7b27f15a76ecb25 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 18 Jul 2026 16:20:10 +0300 Subject: [PATCH 14/52] fix --- res/shaders/effects/deferred_lighting.glsl | 9 ++++++++- res/shaders/lib/fog.glsl | 8 ++++++-- src/graphics/render/CloudsRenderer.cpp | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/res/shaders/effects/deferred_lighting.glsl b/res/shaders/effects/deferred_lighting.glsl index 6207f1583..55e267bf3 100644 --- a/res/shaders/effects/deferred_lighting.glsl +++ b/res/shaders/effects/deferred_lighting.glsl @@ -1,6 +1,9 @@ #include #include +#define CLOUDS_FOG_FACTOR_MUL 0.3f +#define CLOUDS_FOG_CURVE_MUL 0.4f + vec4 effect() { vec4 pos = texture(u_position, v_uv); float light = 1.0; @@ -32,6 +35,10 @@ vec4 effect() { light = pow(light, u_gamma); vec3 fogColor = texture(u_skybox, dir).rgb; - float fog = calc_fog(pow(length(u_view * vec4((modelpos.xyz - u_cameraPos) * FOG_POS_SCALE, 0.0)) / 256.0, (1.0f - emission * 0.7))); + float fog = calc_fog( + length(u_view * vec4((modelpos.xyz - u_cameraPos) * FOG_POS_SCALE, 0.0)) / 256.0, + mix(1.0, CLOUDS_FOG_FACTOR_MUL, emission), + mix(1.0, CLOUDS_FOG_CURVE_MUL, emission) + ); return vec4(mix(texture(u_screen, v_uv).rgb * mix(1.0, light, 1.0), fogColor, fog), 1.0); } diff --git a/res/shaders/lib/fog.glsl b/res/shaders/lib/fog.glsl index e5ff18bbd..597380bb6 100644 --- a/res/shaders/lib/fog.glsl +++ b/res/shaders/lib/fog.glsl @@ -7,13 +7,17 @@ uniform float u_weatherFogOpacity; uniform float u_weatherFogDencity; uniform float u_weatherFogCurve; -float calc_fog(float depth) { +float calc_fog(float depth, float fogFactor, float fogCurve) { return min( 1.0, - max(pow(depth * u_fogFactor, u_fogCurve), + max(pow(depth * u_fogFactor * fogFactor, u_fogCurve * fogCurve), min(pow(depth * u_weatherFogDencity, u_weatherFogCurve), u_weatherFogOpacity)) ); } +float calc_fog(float depth) { + return calc_fog(depth, 1.0, 1.0); +} + #endif // FOG_GLSL_ diff --git a/src/graphics/render/CloudsRenderer.cpp b/src/graphics/render/CloudsRenderer.cpp index e2047eb63..bfc53e4bb 100755 --- a/src/graphics/render/CloudsRenderer.cpp +++ b/src/graphics/render/CloudsRenderer.cpp @@ -329,7 +329,7 @@ void CloudsRenderer::draw( shader.uniform4f("u_tint", glm::vec4(weather.cloudsTint(), 1.0f)); shader.uniform1f("u_fogFactor", fogFactor * 0.03f); - shader.uniform1f("u_fogCurve", 0.7f - 0.3f); + shader.uniform1f("u_fogCurve", 0.4f); for (int i = 0; i < std::min(quality, layers.size()); i++) { draw(layers[i], frustum, shader, camera, timer, i); From 40e73ca34b14939468f64a5d349a98400e704bbb Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 27 Jul 2026 01:18:36 +0300 Subject: [PATCH 15/52] fix 3d texts antialiasing render issue --- src/graphics/render/WorldRenderer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 965f3fcbf..e6e4f67ed 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -233,8 +233,6 @@ void WorldRenderer::renderOpaque( const EngineSettings& settings, bool hudVisible ) { - texts->render(ctx, camera, settings, hudVisible, false); - float fogFactor = calcFogFactor(); auto& entityShader = assets.require("entity"); @@ -341,6 +339,7 @@ void WorldRenderer::renderFrameClassic( bool hudVisible, PostProcessing& postProcessing ) { + const auto& settings = engine.getSettings(); const auto& worldInfo = level.getWorld().getInfo(); DrawContext ctx = pctx.sub(); @@ -351,6 +350,7 @@ void WorldRenderer::renderFrameClassic( skybox->draw( level.environment, ctx, camera, worldInfo.daytime, weather.clouds() ); + texts->render(ctx, camera, settings, hudVisible, false); if (debug && hudVisible) { renderDebugLines(ctx, camera); @@ -404,6 +404,7 @@ void WorldRenderer::renderFrameAdvanced( skybox->draw( level.environment, ctx, camera, worldInfo.daytime, weather.clouds() ); + texts->render(ctx, camera, settings, hudVisible, false); if (debug && hudVisible) { renderDebugLines(ctx, camera); } From 4ae3dcf1ae643e8ee7ce774976e6a78572abb7ed Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 2 Aug 2026 20:07:10 +0300 Subject: [PATCH 16/52] remove it again --- src/graphics/render/ChunksRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index 7e81e0bd5..bf90f8a93 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -127,7 +127,7 @@ const ChunkMesh* ChunksRenderer::render( const std::shared_ptr& chunk, bool important, bool lowPriority ) { glm::ivec2 key(chunk->x, chunk->z); - if (important || true) { + if (important) { ChunkMesh mesh {}; auto voxelsBuffer = prepareVoxelsVolume(*chunk); mesh = renderer->render(chunk.get(), *voxelsBuffer); From 2a2169cff0cd25cf38c4bde95735c2392373dafc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 2 Aug 2026 22:35:03 +0300 Subject: [PATCH 17/52] refactor: meshes build queue instead of retrieveChunk --- src/graphics/render/ChunksRenderer.cpp | 144 ++++++++++++++----------- src/graphics/render/ChunksRenderer.hpp | 53 +++++---- src/maths/util.hpp | 8 +- 3 files changed, 112 insertions(+), 93 deletions(-) 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; } From 7525e31942c5d51a90246b2e04dfa3c87c5c13be Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 3 Aug 2026 00:06:19 +0300 Subject: [PATCH 18/52] cleanup ChunksRenderer --- src/graphics/render/ChunksRenderer.cpp | 34 +++++++++++++++----------- src/graphics/render/ChunksRenderer.hpp | 8 +++--- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index 09544a358..82a7fa6d6 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -125,30 +125,30 @@ std::shared_ptr ChunksRenderer::prepareVoxelsVolume( return voxelsBuffer; } -const ChunkMesh* ChunksRenderer::render( - const std::shared_ptr& chunk, bool important, bool lowPriority +void ChunksRenderer::renderBlocking(const std::shared_ptr& chunk) { + glm::ivec2 key(chunk->x, chunk->z); + ChunkMesh mesh {}; + auto voxelsBuffer = prepareVoxelsVolume(*chunk); + mesh = renderer->render(chunk.get(), *voxelsBuffer); + meshes[key] = std::move(mesh); + chunk->flags.modified = false; +} + +void ChunksRenderer::render( + const std::shared_ptr& chunk, bool lowPriority ) { glm::ivec2 key(chunk->x, chunk->z); - if (important) { - ChunkMesh mesh {}; - auto voxelsBuffer = prepareVoxelsVolume(*chunk); - mesh = renderer->render(chunk.get(), *voxelsBuffer); - meshes[key] = std::move(mesh); - chunk->flags.modified = false; - return &meshes[key]; - } if (inwork.find(key) != inwork.end() || ((inwork.size() >= threadPool.getWorkersCount() || enqueuedInFrame >= MAX_CHUNKS_ENQUEUED_IN_FRAME) && lowPriority)) { - return nullptr; + return; } chunk->flags.modified = false; enqueuedInFrame++; auto voxelsBuffer = prepareVoxelsVolume(*chunk); threadPool.enqueueJob({chunk, std::move(voxelsBuffer)}); inwork[key] = true; - return nullptr; } void ChunksRenderer::unload(const Chunk* chunk) { @@ -195,6 +195,8 @@ void ChunksRenderer::update() { } ); + int loadDistance = settings.chunks.loadDistance.get(); + const int topN = 10; int top = std::min(meshBuildQueue.size(), topN); for (int i = 0; i < top; i++) { @@ -214,12 +216,16 @@ void ChunksRenderer::update() { ) ); bool important = distance < CHUNK_W * 1.5f; - bool lowPriority = distance > CHUNK_W * settings.chunks.loadDistance.get() * 0.5; + bool lowPriority = distance > CHUNK_W * loadDistance * 0.5; if (chunk->flags.dirtyHeights) { chunk->updateHeights(); } - render(chunk, important, lowPriority); + if (important) { + renderBlocking(chunk); + } else { + render(chunk, lowPriority); + } } } diff --git a/src/graphics/render/ChunksRenderer.hpp b/src/graphics/render/ChunksRenderer.hpp index 2f25875c5..5e26adc43 100644 --- a/src/graphics/render/ChunksRenderer.hpp +++ b/src/graphics/render/ChunksRenderer.hpp @@ -54,7 +54,7 @@ public: const ContentGfxCache& cache, const EngineSettings& settings ); - virtual ~ChunksRenderer(); + ~ChunksRenderer(); void unload(const Chunk* chunk); void clear(); @@ -88,7 +88,7 @@ private: std::shared_ptr prepareVoxelsVolume(const Chunk& chunk); - const ChunkMesh* render( - const std::shared_ptr& chunk, bool important, bool lowPriority - ); + void render(const std::shared_ptr& chunk, bool lowPriority); + + void renderBlocking(const std::shared_ptr& chunk); }; From e2a9d2871a588f94a8168401f6b6bda176fe684f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 3 Aug 2026 00:06:31 +0300 Subject: [PATCH 19/52] cleanup --- src/util/ThreadPool.hpp | 1 - src/voxels/Chunk.hpp | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/util/ThreadPool.hpp b/src/util/ThreadPool.hpp index dd692d203..2c743e37d 100644 --- a/src/util/ThreadPool.hpp +++ b/src/util/ThreadPool.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include diff --git a/src/voxels/Chunk.hpp b/src/voxels/Chunk.hpp index 941034073..4b2fa193d 100644 --- a/src/voxels/Chunk.hpp +++ b/src/voxels/Chunk.hpp @@ -29,16 +29,16 @@ public: voxel voxels[CHUNK_VOL] {}; std::shared_ptr lightmap; struct { - bool modified : 1; - bool ready : 1; - bool loaded : 1; - bool lighted : 1; - bool unsaved : 1; - bool loadedLights : 1; - bool entities : 1; - bool blocksData : 1; - bool dirtyHeights : 1; - bool inventoriesRemoved : 1; + bool modified : 1; // is chunk mesh should be updated + bool ready : 1; // is chunk ready for modifications (loaded / generated) + bool loaded : 1; // was chunk loaded from region + bool lighted : 1; // is lights built (chunk ready to be visualized) + bool unsaved : 1; // does chunk contain unsaved changes + bool loadedLights : 1; // was lights loaded from cache + bool entities : 1; // does chunk contain entities list changes + bool blocksData : 1; // does chunk contain block fields changes + bool dirtyHeights : 1; // is chunk bottom, top should be recalculated + bool inventoriesRemoved : 1; // was block inventories removed since the last save } flags {}; uint64_t lastRandomTickId = -1; From 2a079789cc5e3db18186687bab92798bbf7621f4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 9 Aug 2026 18:03:57 +0300 Subject: [PATCH 20/52] update .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index c0b0e1c9d..4d0155832 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,6 @@ appimage-build/ # libs /libs/ /vcpkg_installed/ + +# vcstudio +.vcstudio From 3f68ed5fe4318e734b4b85b46bbeff82dc71d442 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 9 Aug 2026 19:50:09 +0300 Subject: [PATCH 21/52] add selectbox 'button' mode --- src/graphics/ui/elements/SelectBox.cpp | 29 ++++++++++++++++++++------ src/graphics/ui/elements/SelectBox.hpp | 11 ++++++++++ src/graphics/ui/gui_xml.cpp | 12 +++++++++-- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/graphics/ui/elements/SelectBox.cpp b/src/graphics/ui/elements/SelectBox.cpp index b8205d806..1ac09ad24 100644 --- a/src/graphics/ui/elements/SelectBox.cpp +++ b/src/graphics/ui/elements/SelectBox.cpp @@ -13,14 +13,17 @@ SelectBox::SelectBox( GUI& gui, std::vector