From 163a0a8c93f6718d8ef0e7d0cb31eed1f8e4831e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 21 Dec 2025 19:05:14 +0300 Subject: [PATCH 01/42] rename ImageFormat enum items --- src/coders/png.cpp | 10 +-- src/frontend/hud.cpp | 2 +- src/graphics/core/Atlas.cpp | 2 +- src/graphics/core/Batch2D.cpp | 2 +- src/graphics/core/Batch3D.cpp | 2 +- src/graphics/core/GBuffer.cpp | 2 +- src/graphics/core/ImageData.cpp | 62 +++++++++---------- src/graphics/core/ImageData.hpp | 6 +- src/graphics/core/Texture.cpp | 2 +- src/graphics/core/gl_util.hpp | 4 +- src/graphics/render/BlocksRenderer.hpp | 8 +-- src/graphics/render/MainBatch.cpp | 2 +- src/graphics/render/Skybox.cpp | 2 +- src/graphics/ui/gui_xml.cpp | 2 +- .../lua/usertypes/lua_type_canvas.cpp | 2 +- .../lua/usertypes/lua_type_heightmap.cpp | 2 +- src/window/detail/GLFWWindow.cpp | 2 +- test/world/generator/SurroundMap.cpp | 2 +- 18 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/coders/png.cpp b/src/coders/png.cpp index 7c469e32f..030844352 100644 --- a/src/coders/png.cpp +++ b/src/coders/png.cpp @@ -251,13 +251,13 @@ std::unique_ptr png::load_image(const ubyte* bytes, size_t size) { } png_read_image(pngPtr, rowPointers.get()); - ImageFormat format = ImageFormat::rgba8888; + ImageFormat format = ImageFormat::RGBA8888; switch (colorType) { case PNG_COLOR_TYPE_RGBA: - format = ImageFormat::rgba8888; + format = ImageFormat::RGBA8888; break; case PNG_COLOR_TYPE_RGB: - format = ImageFormat::rgb888; + format = ImageFormat::RGB888; break; default: png_destroy_read_struct(&pngPtr, &infoPtr, nullptr); @@ -296,7 +296,7 @@ void png::write_image(const std::string& filename, const ImageData* image) { image->getWidth(), image->getHeight(), (const ubyte*)image->getData(), - image->getFormat() == ImageFormat::rgba8888 + image->getFormat() == ImageFormat::RGBA8888 ); } @@ -306,6 +306,6 @@ util::Buffer png::encode_image(const ImageData& image) { image.getWidth(), image.getHeight(), image.getData(), - format == ImageFormat::rgba8888 + format == ImageFormat::RGBA8888 ); } diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index d40ab3b8e..92ff3a370 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -157,7 +157,7 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player) frontend(frontend), player(player), debugImgWorldGen(std::make_unique( - ImageFormat::rgba8888, WORLDGEN_IMG_SIZE, WORLDGEN_IMG_SIZE + ImageFormat::RGBA8888, WORLDGEN_IMG_SIZE, WORLDGEN_IMG_SIZE )) { contentAccess = createContentAccess(); contentAccess->setId("hud.content-access"); diff --git a/src/graphics/core/Atlas.cpp b/src/graphics/core/Atlas.cpp index d06a20b98..092d6fedf 100644 --- a/src/graphics/core/Atlas.cpp +++ b/src/graphics/core/Atlas.cpp @@ -95,7 +95,7 @@ std::unique_ptr AtlasBuilder::build(uint extrusion, bool prepare, uint ma } } - auto canvas = std::make_unique(ImageFormat::rgba8888, width, height); + auto canvas = std::make_unique(ImageFormat::RGBA8888, width, height); std::unordered_map regions; std::vector rects = packer.getResult(); for (uint i = 0; i < entries.size(); i++) { diff --git a/src/graphics/core/Batch2D.cpp b/src/graphics/core/Batch2D.cpp index b39866cef..7aa856f3f 100644 --- a/src/graphics/core/Batch2D.cpp +++ b/src/graphics/core/Batch2D.cpp @@ -15,7 +15,7 @@ Batch2D::Batch2D(size_t capacity) : capacity(capacity), color(1.0f){ const ubyte pixels[] = { 0xFF, 0xFF, 0xFF, 0xFF }; - ImageData image(ImageFormat::rgba8888, 1, 1, pixels); + ImageData image(ImageFormat::RGBA8888, 1, 1, pixels); blank = Texture::from(&image); currentTexture = nullptr; } diff --git a/src/graphics/core/Batch3D.cpp b/src/graphics/core/Batch3D.cpp index df6fdef74..ce9f176c7 100644 --- a/src/graphics/core/Batch3D.cpp +++ b/src/graphics/core/Batch3D.cpp @@ -20,7 +20,7 @@ Batch3D::Batch3D(size_t capacity) const ubyte pixels[] = { 255, 255, 255, 255, }; - ImageData image(ImageFormat::rgba8888, 1, 1, pixels); + ImageData image(ImageFormat::RGBA8888, 1, 1, pixels); blank = Texture::from(&image); currentTexture = nullptr; } diff --git a/src/graphics/core/GBuffer.cpp b/src/graphics/core/GBuffer.cpp index d466f1e99..aba3103fc 100644 --- a/src/graphics/core/GBuffer.cpp +++ b/src/graphics/core/GBuffer.cpp @@ -260,7 +260,7 @@ std::unique_ptr GBuffer::toImage() const { glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, data.get()); glBindTexture(GL_TEXTURE_2D, 0); return std::make_unique( - ImageFormat::rgb888, width, height, std::move(data) + ImageFormat::RGB888, width, height, std::move(data) ); } diff --git a/src/graphics/core/ImageData.cpp b/src/graphics/core/ImageData.cpp index 8a44068dc..6875d7ab9 100644 --- a/src/graphics/core/ImageData.cpp +++ b/src/graphics/core/ImageData.cpp @@ -11,8 +11,8 @@ ImageData::ImageData(ImageFormat format, uint width, uint height) : format(format), width(width), height(height) { size_t pixsize; switch (format) { - case ImageFormat::rgb888: pixsize = 3; break; - case ImageFormat::rgba8888: pixsize = 4; break; + case ImageFormat::RGB888: pixsize = 3; break; + case ImageFormat::RGBA8888: pixsize = 4; break; default: throw std::runtime_error("format is not supported"); } @@ -27,8 +27,8 @@ ImageData::ImageData(ImageFormat format, uint width, uint height, const ubyte* d : format(format), width(width), height(height) { size_t pixsize; switch (format) { - case ImageFormat::rgb888: pixsize = 3; break; - case ImageFormat::rgba8888: pixsize = 4; break; + case ImageFormat::RGB888: pixsize = 3; break; + case ImageFormat::RGBA8888: pixsize = 4; break; default: throw std::runtime_error("format is not supported"); } @@ -40,9 +40,9 @@ ImageData::~ImageData() = default; void ImageData::flipX() { switch (format) { - case ImageFormat::rgb888: - case ImageFormat::rgba8888: { - uint size = (format == ImageFormat::rgba8888) ? 4 : 3; + case ImageFormat::RGB888: + case ImageFormat::RGBA8888: { + uint size = (format == ImageFormat::RGBA8888) ? 4 : 3; for (uint y = 0; y < height; y++) { for (uint x = 0; x < width / 2; x++) { for (uint c = 0; c < size; c++) { @@ -62,9 +62,9 @@ void ImageData::flipX() { void ImageData::flipY() { switch (format) { - case ImageFormat::rgb888: - case ImageFormat::rgba8888: { - uint size = (format == ImageFormat::rgba8888) ? 4 : 3; + case ImageFormat::RGB888: + case ImageFormat::RGBA8888: { + uint size = (format == ImageFormat::RGBA8888) ? 4 : 3; for (uint y = 0; y < height/2; y++) { for (uint x = 0; x < width; x++) { for (uint c = 0; c < size; c++) { @@ -87,8 +87,8 @@ void ImageData::blit(const ImageData& image, int x, int y) { blitMatchingFormat(image, x, y); return; } - if (format == ImageFormat::rgba8888 && - image.format == ImageFormat::rgb888) { + if (format == ImageFormat::RGBA8888 && + image.format == ImageFormat::RGB888) { blitRGB_on_RGBA(image, x, y); return; } @@ -188,10 +188,10 @@ static void draw_line(ImageData& image, int x1, int y1, int x2, int y2, const gl void ImageData::drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color) { switch (format) { - case ImageFormat::rgb888: + case ImageFormat::RGB888: draw_line<3>(*this, x1, y1, x2, y2, color); break; - case ImageFormat::rgba8888: + case ImageFormat::RGBA8888: draw_line<4>(*this, x1, y1, x2, y2, color); break; default: @@ -223,10 +223,10 @@ static void draw_rect(ImageData& image, int dstX, int dstY, int width, int heigh void ImageData::drawRect(int x, int y, int width, int height, const glm::ivec4& color) { switch (format) { - case ImageFormat::rgb888: + case ImageFormat::RGB888: draw_rect<3>(*this, x, y, width, height, color); break; - case ImageFormat::rgba8888: + case ImageFormat::RGBA8888: draw_rect<4>(*this, x, y, width, height, color); break; default: @@ -260,8 +260,8 @@ void ImageData::blitRGB_on_RGBA(const ImageData& image, int x, int y) { void ImageData::blitMatchingFormat(const ImageData& image, int x, int y) { uint comps; switch (format) { - case ImageFormat::rgb888: comps = 3; break; - case ImageFormat::rgba8888: comps = 4; break; + case ImageFormat::RGB888: comps = 3; break; + case ImageFormat::RGBA8888: comps = 4; break; default: throw std::runtime_error("only unsigned byte formats supported"); } @@ -295,8 +295,8 @@ void ImageData::blitMatchingFormat(const ImageData& image, int x, int y) { void ImageData::extrude(int x, int y, int w, int h) { uint comps; switch (format) { - case ImageFormat::rgb888: comps = 3; break; - case ImageFormat::rgba8888: comps = 4; break; + case ImageFormat::RGB888: comps = 3; break; + case ImageFormat::RGBA8888: comps = 4; break; default: throw std::runtime_error("only unsigned byte formats supported"); } @@ -431,8 +431,8 @@ static void check_matching(const ImageData& a, const ImageData& b) { void ImageData::mulColor(const glm::ivec4& color) { uint comps; switch (format) { - case ImageFormat::rgb888: comps = 3; break; - case ImageFormat::rgba8888: comps = 4; break; + case ImageFormat::RGB888: comps = 3; break; + case ImageFormat::RGBA8888: comps = 4; break; default: throw std::runtime_error("only unsigned byte formats supported"); } @@ -453,8 +453,8 @@ void ImageData::addColor(const ImageData& other, int multiplier) { uint comps; switch (format) { - case ImageFormat::rgb888: comps = 3; break; - case ImageFormat::rgba8888: comps = 4; break; + case ImageFormat::RGB888: comps = 3; break; + case ImageFormat::RGBA8888: comps = 4; break; default: throw std::runtime_error("only unsigned byte formats supported"); } @@ -473,8 +473,8 @@ void ImageData::addColor(const ImageData& other, int multiplier) { void ImageData::extend(int newWidth, int newHeight) { size_t comps; switch (format) { - case ImageFormat::rgb888: comps = 3; break; - case ImageFormat::rgba8888: comps = 4; break; + case ImageFormat::RGB888: comps = 3; break; + case ImageFormat::RGBA8888: comps = 4; break; default: throw std::runtime_error("only unsigned byte formats supported"); } @@ -499,8 +499,8 @@ void ImageData::extend(int newWidth, int newHeight) { void ImageData::addColor(const glm::ivec4& color, int multiplier) { uint comps; switch (format) { - case ImageFormat::rgb888: comps = 3; break; - case ImageFormat::rgba8888: comps = 4; break; + case ImageFormat::RGB888: comps = 3; break; + case ImageFormat::RGBA8888: comps = 4; break; default: throw std::runtime_error("only unsigned byte formats supported"); } @@ -521,8 +521,8 @@ void ImageData::mulColor(const ImageData& other) { uint comps; switch (format) { - case ImageFormat::rgb888: comps = 3; break; - case ImageFormat::rgba8888: comps = 4; break; + case ImageFormat::RGB888: comps = 3; break; + case ImageFormat::RGBA8888: comps = 4; break; default: throw std::runtime_error("only unsigned byte formats supported"); } @@ -541,7 +541,7 @@ void ImageData::mulColor(const ImageData& other) { std::unique_ptr add_atlas_margins(ImageData* image, int grid_size) { // RGBA is only supported - assert(image->getFormat() == ImageFormat::rgba8888); + assert(image->getFormat() == ImageFormat::RGBA8888); assert(image->getWidth() == image->getHeight()); int srcwidth = image->getWidth(); diff --git a/src/graphics/core/ImageData.hpp b/src/graphics/core/ImageData.hpp index 94987886e..8f4a81c60 100644 --- a/src/graphics/core/ImageData.hpp +++ b/src/graphics/core/ImageData.hpp @@ -6,8 +6,8 @@ #include enum class ImageFormat { - rgb888, - rgba8888 + RGB888, + RGBA8888 }; class ImageData { @@ -57,7 +57,7 @@ public: } size_t getDataSize() const { - size_t channels = 3 + (format == ImageFormat::rgba8888); + size_t channels = 3 + (format == ImageFormat::RGBA8888); return width * height * channels; } }; diff --git a/src/graphics/core/Texture.cpp b/src/graphics/core/Texture.cpp index dcd29e372..d60bd2779 100644 --- a/src/graphics/core/Texture.cpp +++ b/src/graphics/core/Texture.cpp @@ -68,7 +68,7 @@ std::unique_ptr Texture::readData() { glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.get()); glBindTexture(GL_TEXTURE_2D, 0); return std::make_unique( - ImageFormat::rgba8888, width, height, std::move(data) + ImageFormat::RGBA8888, width, height, std::move(data) ); } diff --git a/src/graphics/core/gl_util.hpp b/src/graphics/core/gl_util.hpp index da2abfb5a..a21c8d8e9 100644 --- a/src/graphics/core/gl_util.hpp +++ b/src/graphics/core/gl_util.hpp @@ -9,8 +9,8 @@ namespace gl { inline GLenum to_glenum(ImageFormat imageFormat) { switch (imageFormat) { - case ImageFormat::rgb888: return GL_RGB; - case ImageFormat::rgba8888: return GL_RGBA; + case ImageFormat::RGB888: return GL_RGB; + case ImageFormat::RGBA8888: return GL_RGBA; default: return 0; } diff --git a/src/graphics/render/BlocksRenderer.hpp b/src/graphics/render/BlocksRenderer.hpp index 2006baab3..65d45f134 100644 --- a/src/graphics/render/BlocksRenderer.hpp +++ b/src/graphics/render/BlocksRenderer.hpp @@ -1,10 +1,7 @@ #pragma once -#include -#include -#include "voxels/voxel.hpp" #include "typedefs.hpp" - +#include "voxels/voxel.hpp" #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" #include "voxels/VoxelsVolume.hpp" @@ -12,6 +9,9 @@ #include "commons.hpp" #include "settings.hpp" +#include +#include + template class Mesh; class Content; class Block; diff --git a/src/graphics/render/MainBatch.cpp b/src/graphics/render/MainBatch.cpp index 5b08ffe04..1a501ca15 100644 --- a/src/graphics/render/MainBatch.cpp +++ b/src/graphics/render/MainBatch.cpp @@ -16,7 +16,7 @@ MainBatch::MainBatch(size_t capacity) const ubyte pixels[] = { 255, 255, 255, 255, }; - ImageData image(ImageFormat::rgba8888, 1, 1, pixels); + ImageData image(ImageFormat::RGBA8888, 1, 1, pixels); blank = Texture::from(&image); } diff --git a/src/graphics/render/Skybox.cpp b/src/graphics/render/Skybox.cpp index 149acb108..b5c5191f2 100644 --- a/src/graphics/render/Skybox.cpp +++ b/src/graphics/render/Skybox.cpp @@ -28,7 +28,7 @@ Skybox::Skybox(uint size, Shader& shader) shader(shader), batch3d(std::make_unique(4096)) { - auto cubemap = std::make_unique(size, size, ImageFormat::rgb888); + auto cubemap = std::make_unique(size, size, ImageFormat::RGB888); uint fboid; glGenFramebuffers(1, &fboid); diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index 9b40c3b72..db8df0511 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -650,7 +650,7 @@ static std::shared_ptr read_canvas( size = element.attr("size").asVec2(); } auto image = - std::make_shared(reader.getGUI(), ImageFormat::rgba8888, size); + std::make_shared(reader.getGUI(), ImageFormat::RGBA8888, size); read_uinode(reader, element, *image); return image; } diff --git a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp index a73172923..37c53abd9 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp @@ -401,7 +401,7 @@ static int l_meta_meta_call(lua::State* L) { return newuserdata( L, nullptr, - std::make_shared(ImageFormat::rgba8888, size.x, size.y) + std::make_shared(ImageFormat::RGBA8888, size.x, size.y) ); } diff --git a/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp b/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp index d65282982..9286467d3 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_heightmap.cpp @@ -56,7 +56,7 @@ static int l_dump(lua::State* L) { io::path file = require_string(L, 2); uint w = heightmap->getWidth(); uint h = heightmap->getHeight(); - ImageData image(ImageFormat::rgb888, w, h); + ImageData image(ImageFormat::RGB888, w, h); auto heights = heightmap->getValues(); auto raster = image.getData(); for (uint y = 0; y < h; y++) { diff --git a/src/window/detail/GLFWWindow.cpp b/src/window/detail/GLFWWindow.cpp index 074693066..a2f2a3733 100644 --- a/src/window/detail/GLFWWindow.cpp +++ b/src/window/detail/GLFWWindow.cpp @@ -561,7 +561,7 @@ public: glPixelStorei(GL_PACK_ALIGNMENT, 1); glReadPixels(0, 0, size.x, size.y, GL_RGB, GL_UNSIGNED_BYTE, data.get()); return std::make_unique( - ImageFormat::rgb888, size.x, size.y, data.release() + ImageFormat::RGB888, size.x, size.y, data.release() ); } diff --git a/test/world/generator/SurroundMap.cpp b/test/world/generator/SurroundMap.cpp index 6fb5d7ae2..45294fa99 100644 --- a/test/world/generator/SurroundMap.cpp +++ b/test/world/generator/SurroundMap.cpp @@ -46,7 +46,7 @@ void visualize(const SurroundMap& map, int mul, int max) { int ox = areaMap.getOffsetX(); int oy = areaMap.getOffsetY(); - ImageData image(ImageFormat::rgb888, w, h); + ImageData image(ImageFormat::RGB888, w, h); ubyte* bytes = image.getData(); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { From 99fe787cba0116e64a4f3d212b85598f3f7559fe Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 21 Dec 2025 19:08:23 +0300 Subject: [PATCH 02/42] add PaletteAtlasBuilder --- src/graphics/core/PaletteAtlasBuilder.cpp | 26 +++++++++++++++++++++++ src/graphics/core/PaletteAtlasBuilder.hpp | 22 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/graphics/core/PaletteAtlasBuilder.cpp create mode 100644 src/graphics/core/PaletteAtlasBuilder.hpp diff --git a/src/graphics/core/PaletteAtlasBuilder.cpp b/src/graphics/core/PaletteAtlasBuilder.cpp new file mode 100644 index 000000000..b87a3cb35 --- /dev/null +++ b/src/graphics/core/PaletteAtlasBuilder.cpp @@ -0,0 +1,26 @@ +#include "PaletteAtlasBuilder.hpp" + +#include "../core/Atlas.hpp" +#include "../core/ImageData.hpp" + +PaletteAtlasBuilder::PaletteAtlasBuilder() = default; + +void PaletteAtlasBuilder::put(const std::string& name, const glm::vec4& color) { + entries[name] = { glm::clamp(color, glm::vec4(0.0f), glm::vec4(1.0f)) }; +} + +std::unique_ptr PaletteAtlasBuilder::build() const { + AtlasBuilder builder; + for (const auto& [name, entry] : entries) { + ubyte data[4] { + static_cast(entry.color.r * 255), + static_cast(entry.color.g * 255), + static_cast(entry.color.b * 255), + static_cast(entry.color.a * 255), + }; + builder.add(name, std::make_unique( + ImageFormat::RGBA8888, 1, 1, data + )); + } + return builder.build(1, true); +} diff --git a/src/graphics/core/PaletteAtlasBuilder.hpp b/src/graphics/core/PaletteAtlasBuilder.hpp new file mode 100644 index 000000000..7fd2077ec --- /dev/null +++ b/src/graphics/core/PaletteAtlasBuilder.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +class Atlas; + +class PaletteAtlasBuilder { +public: + struct Entry { + glm::vec4 color; + }; + + PaletteAtlasBuilder(); + + void put(const std::string& name, const glm::vec4& color); + + std::unique_ptr build() const; +private: + std::map entries; +}; From 7e51f47044f33701001f978f71c5bfb31c4677a6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 21 Dec 2025 19:11:07 +0300 Subject: [PATCH 03/42] reformat BlocksRenderer --- src/graphics/render/BlocksRenderer.hpp | 41 +++++++++++++------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/graphics/render/BlocksRenderer.hpp b/src/graphics/render/BlocksRenderer.hpp index 65d45f134..56a24b472 100644 --- a/src/graphics/render/BlocksRenderer.hpp +++ b/src/graphics/render/BlocksRenderer.hpp @@ -22,6 +22,27 @@ class ContentGfxCache; struct UVRegion; class BlocksRenderer { +public: + BlocksRenderer( + size_t capacity, + const Content& content, + const ContentGfxCache& cache, + const EngineSettings& settings + ); + virtual ~BlocksRenderer(); + + void build(const Chunk* chunk, const VoxelsVolume& volume); + ChunkMesh render( + const Chunk* chunk, const VoxelsVolume& volume + ); + ChunkMeshData createMesh(); + + size_t getMemoryConsumption() const; + + bool isCancelled() const { + return cancelled; + } +private: static const glm::vec3 SUN_VECTOR; const Content& content; std::unique_ptr vertexBuffer; @@ -160,24 +181,4 @@ class BlocksRenderer { void render(const voxel* voxels, const int beginEnds[256][2]); SortingMeshData renderTranslucent(const voxel* voxels, int beginEnds[256][2]); -public: - BlocksRenderer( - size_t capacity, - const Content& content, - const ContentGfxCache& cache, - const EngineSettings& settings - ); - virtual ~BlocksRenderer(); - - void build(const Chunk* chunk, const VoxelsVolume& volume); - ChunkMesh render( - const Chunk* chunk, const VoxelsVolume& volume - ); - ChunkMeshData createMesh(); - - size_t getMemoryConsumption() const; - - bool isCancelled() const { - return cancelled; - } }; From 4b4579858b3dda5ccf4ba32509222f2585901557 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 21 Dec 2025 19:25:45 +0300 Subject: [PATCH 04/42] fix windows build --- src/graphics/core/PaletteAtlasBuilder.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/graphics/core/PaletteAtlasBuilder.hpp b/src/graphics/core/PaletteAtlasBuilder.hpp index 7fd2077ec..348625c50 100644 --- a/src/graphics/core/PaletteAtlasBuilder.hpp +++ b/src/graphics/core/PaletteAtlasBuilder.hpp @@ -2,6 +2,7 @@ #include #include +#include #include class Atlas; From 79eb18553053014ccbbd30361f6a1a1a6f4f7dc6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 25 Dec 2025 23:39:11 +0300 Subject: [PATCH 05/42] reduce copies in ThreadPool --- src/assets/AssetsLoader.cpp | 2 +- src/graphics/render/BlocksRenderer.cpp | 2 +- src/graphics/render/BlocksRenderer.hpp | 4 ++-- src/graphics/render/ChunksRenderer.cpp | 24 +++++++++++++----------- src/graphics/render/commons.hpp | 8 ++++---- src/util/ThreadPool.hpp | 19 +++++++++++++------ src/world/files/WorldConverter.cpp | 2 +- 7 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index 49ce5e04d..f717c1e5b 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -354,7 +354,7 @@ std::shared_ptr AssetsLoader::startTask(runnable onDone) { std::make_shared>( "assets-loader-pool", [=]() { return std::make_shared(this); }, - [this](const assetload::postfunc& func) { func(&assets); } + [this](assetload::postfunc&& func) { func(&assets); } ); pool->setOnComplete(std::move(onDone)); while (!entries.empty()) { diff --git a/src/graphics/render/BlocksRenderer.cpp b/src/graphics/render/BlocksRenderer.cpp index 5320edd73..64f8811f1 100644 --- a/src/graphics/render/BlocksRenderer.cpp +++ b/src/graphics/render/BlocksRenderer.cpp @@ -785,7 +785,7 @@ ChunkMesh BlocksRenderer::render( IndexBufferData {indexBuffer.get(), indexCount}, IndexBufferData {denseIndexBuffer.get(), denseIndexCount}, } - ), std::move(sortingMesh)}; + ), std::move(sortingMesh), nullptr}; } size_t BlocksRenderer::getMemoryConsumption() const { diff --git a/src/graphics/render/BlocksRenderer.hpp b/src/graphics/render/BlocksRenderer.hpp index 56a24b472..1eee81783 100644 --- a/src/graphics/render/BlocksRenderer.hpp +++ b/src/graphics/render/BlocksRenderer.hpp @@ -21,7 +21,7 @@ class VoxelsVolume; class ContentGfxCache; struct UVRegion; -class BlocksRenderer { +class BlocksRenderer final { public: BlocksRenderer( size_t capacity, @@ -29,7 +29,7 @@ public: const ContentGfxCache& cache, const EngineSettings& settings ); - virtual ~BlocksRenderer(); + ~BlocksRenderer(); void build(const Chunk* chunk, const VoxelsVolume& volume); ChunkMesh render( diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index 78bf6f2e7..b906b9b5e 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -73,14 +73,16 @@ ChunksRenderer::ChunksRenderer( *level, cache, settings ); }, - [&](RendererResult& result) { - if (!result.cancelled) { - auto meshData = std::move(result.meshData); - meshes[result.key] = ChunkMesh { - std::make_unique>(meshData.mesh), - std::move(meshData.sortingMesh)}; - } - inwork.erase(result.key); + [&](RendererResult&& result) { + if (!result.cancelled) { + auto meshData = std::move(result.meshData); + auto chunk = std::make_unique>(meshData.mesh); + meshes[result.key] = ChunkMesh { + std::move(chunk), + std::move(meshData.sortingMesh), + nullptr}; + } + inwork.erase(result.key); }, settings.graphics.chunkMaxRenderers.get() ) { @@ -130,8 +132,8 @@ const Mesh* ChunksRenderer::render( auto voxelsBuffer = prepareVoxelsVolume(*chunk); auto mesh = renderer->render(chunk.get(), *voxelsBuffer); - meshes[key] = - ChunkMesh {std::move(mesh.mesh), std::move(mesh.sortingMeshData)}; + meshes[key] = ChunkMesh { + std::move(mesh.mesh), std::move(mesh.sortingMeshData), nullptr}; return meshes[key].mesh.get(); } if (inwork.find(key) != inwork.end()) { @@ -174,7 +176,7 @@ const Mesh* ChunksRenderer::getOrRender( } void ChunksRenderer::update() { - threadPool.update(); + threadPool.pullResults(); } const Mesh* ChunksRenderer::retrieveChunk( diff --git a/src/graphics/render/commons.hpp b/src/graphics/render/commons.hpp index 9e78d1f53..5118f9c7d 100644 --- a/src/graphics/render/commons.hpp +++ b/src/graphics/render/commons.hpp @@ -1,14 +1,14 @@ #pragma once +#include "graphics/core/MeshData.hpp" +#include "util/Buffer.hpp" + #include #include #include #include #include -#include "graphics/core/MeshData.hpp" -#include "util/Buffer.hpp" - /// @brief Chunk mesh vertex format struct ChunkVertex { glm::vec3 position; @@ -49,5 +49,5 @@ struct ChunkMeshData { struct ChunkMesh { std::unique_ptr> mesh; SortingMeshData sortingMeshData; - std::unique_ptr > sortedMesh = nullptr; + std::unique_ptr > sortedMesh; }; diff --git a/src/util/ThreadPool.hpp b/src/util/ThreadPool.hpp index 98ebcce5b..90b7aad14 100644 --- a/src/util/ThreadPool.hpp +++ b/src/util/ThreadPool.hpp @@ -42,7 +42,7 @@ namespace util { std::condition_variable jobsMutexCondition; std::mutex jobsMutex; std::vector> workersBlocked; - consumer resultConsumer; + consumer resultConsumer; consumer onJobFailed = nullptr; runnable onComplete = nullptr; std::atomic busyWorkers = 0; @@ -116,7 +116,7 @@ namespace util { ThreadPool( std::string name, supplier>> workersSupplier, - consumer resultConsumer, + consumer resultConsumer, int maxWorkers=UNLIMITED ) : logger(std::move(name)), resultConsumer(resultConsumer) { @@ -178,22 +178,28 @@ namespace util { } void update() override { + pullResults(); + } + + size_t pullResults(size_t maxResults = -1) { if (!working) { - return; + return 0; } if (failed) { throw std::runtime_error("some job failed"); } bool complete = false; + size_t resultsProcessed = 0; { std::lock_guard lock(resultsMutex); - while (!results.empty()) { - ThreadPoolResult entry = results.front(); + while (!results.empty() && resultsProcessed < maxResults) { + ThreadPoolResult entry = std::move(results.front()); results.pop(); + ++resultsProcessed; try { - resultConsumer(entry.entry); + resultConsumer(std::move(entry.entry)); } catch (std::exception& err) { logger.error() << err.what(); if (onJobFailed) { @@ -227,6 +233,7 @@ namespace util { if (complete) { terminate(); } + return resultsProcessed; } void enqueueJob(T job) { diff --git a/src/world/files/WorldConverter.cpp b/src/world/files/WorldConverter.cpp index 2f116d22e..431d4c41c 100644 --- a/src/world/files/WorldConverter.cpp +++ b/src/world/files/WorldConverter.cpp @@ -163,7 +163,7 @@ std::shared_ptr WorldConverter::startTask( auto pool = std::make_shared>( "converter-pool", [=]() { return std::make_shared(converter); }, - [=](int&) {} + [=](int&&) {} ); auto& converterTasks = converter->tasks; while (!converterTasks.empty()) { From ddd4f4f24fb06575fcfb71717eb2d1fdbe518e5b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 28 Dec 2025 18:27:24 +0300 Subject: [PATCH 06/42] add common blank texture file --- res/preload.json | 3 ++- res/textures/misc/blank.png | Bin 0 -> 82 bytes 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 res/textures/misc/blank.png diff --git a/res/preload.json b/res/preload.json index 0cd11b5bc..558fb1f0e 100644 --- a/res/preload.json +++ b/res/preload.json @@ -48,7 +48,8 @@ "gui/world", "gui/hud", "gui/entity", - "gui/half_block" + "gui/half_block", + "misc/blank" ], "fonts": [ { diff --git a/res/textures/misc/blank.png b/res/textures/misc/blank.png new file mode 100644 index 0000000000000000000000000000000000000000..d057ee32de8eb35acda495e6bca265588dacb6c6 GIT binary patch literal 82 zcmeAS@N?(olHy`uVBq!ia0vp^j3CU&3?x-=hn)ga%mF?juK)l4Uw%aT1dz++>Eal| caXmQ!$Yx?-e5AO?7|3GqboFyt=akR{0J&omJOBUy literal 0 HcmV?d00001 From f47ccd5f307d083de8606004d13f6e11e2b53e92 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 28 Dec 2025 20:40:40 +0300 Subject: [PATCH 07/42] fix --- src/util/ThreadPool.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/ThreadPool.hpp b/src/util/ThreadPool.hpp index 90b7aad14..4369d2856 100644 --- a/src/util/ThreadPool.hpp +++ b/src/util/ThreadPool.hpp @@ -219,7 +219,7 @@ namespace util { } } - if (onComplete && busyWorkers == 0) { + if (onComplete && busyWorkers == 0 && results.empty()) { std::lock_guard jobsLock(jobsMutex); if (jobs.empty()) { onComplete(); From 88544183d054f81785b0a41fda0b4bb8278d522b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 29 Dec 2025 20:41:47 +0300 Subject: [PATCH 08/42] stabilize delays of chunks meshes building --- src/assets/AssetsLoader.cpp | 2 +- src/graphics/render/BlocksRenderer.cpp | 71 ++++----- src/graphics/render/BlocksRenderer.hpp | 9 +- src/graphics/render/ChunksRenderer.cpp | 44 +++--- src/graphics/render/ChunksRenderer.hpp | 10 +- src/graphics/render/commons.hpp | 10 ++ src/util/ObjectsPool.hpp | 4 + src/util/ThreadPool.hpp | 8 +- src/voxels/Chunks.cpp | 196 ++++++++++++++++--------- src/voxels/Chunks.hpp | 31 +++- src/voxels/VoxelsVolume.cpp | 28 ---- src/voxels/VoxelsVolume.hpp | 111 ++++++++++++-- src/world/files/WorldConverter.cpp | 2 +- 13 files changed, 335 insertions(+), 191 deletions(-) delete mode 100644 src/voxels/VoxelsVolume.cpp diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index f717c1e5b..a6af6067f 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -353,7 +353,7 @@ std::shared_ptr AssetsLoader::startTask(runnable onDone) { auto pool = std::make_shared>( "assets-loader-pool", - [=]() { return std::make_shared(this); }, + [=]() { return std::make_unique(this); }, [this](assetload::postfunc&& func) { func(&assets); } ); pool->setOnComplete(std::move(onDone)); diff --git a/src/graphics/render/BlocksRenderer.cpp b/src/graphics/render/BlocksRenderer.cpp index 64f8811f1..16d8cf4a0 100644 --- a/src/graphics/render/BlocksRenderer.cpp +++ b/src/graphics/render/BlocksRenderer.cpp @@ -42,21 +42,22 @@ void BlocksRenderer::vertex( const glm::vec3& normal, float emission ) { - vertexBuffer[vertexCount].position = coord; - vertexBuffer[vertexCount].uv = {u,v}; - - vertexBuffer[vertexCount].normal[0] = static_cast(normal.r * 127 + 128); - vertexBuffer[vertexCount].normal[1] = static_cast(normal.g * 127 + 128); - vertexBuffer[vertexCount].normal[2] = static_cast(normal.b * 127 + 128); - vertexBuffer[vertexCount].normal[3] = static_cast(emission * 255); - - vertexBuffer[vertexCount].color[0] = static_cast(light.r * 255); - vertexBuffer[vertexCount].color[1] = static_cast(light.g * 255); - vertexBuffer[vertexCount].color[2] = static_cast(light.b * 255); - vertexBuffer[vertexCount].color[3] = static_cast(light.a * 255); - - vertexCount++; + vertexBuffer[vertexCount++] = { + coord, + {u, v}, + { + static_cast(light.r * 255), + static_cast(light.g * 255), + static_cast(light.b * 255), + static_cast(light.a * 255), + }, { + static_cast(normal.x * 127 + 128), + static_cast(normal.y * 127 + 128), + static_cast(normal.z * 127 + 128), + static_cast(emission * 255) + } + }; } void BlocksRenderer::index(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f) { @@ -434,28 +435,11 @@ void BlocksRenderer::blockCube( } } -bool BlocksRenderer::isOpenForLight(int x, int y, int z) const { - blockid_t id = voxelsBuffer->pickBlockId(chunk->x * CHUNK_W + x, - y, - chunk->z * CHUNK_D + z); - if (id == BLOCK_VOID) { - return false; - } - const Block& block = *blockDefsCache[id]; - if (block.lightPassing) { - return true; - } - return !id; -} - glm::vec4 BlocksRenderer::pickLight(int x, int y, int z) const { - if (isOpenForLight(x, y, z)) { - light_t light = voxelsBuffer->pickLight(chunk->x * CHUNK_W + x, y, - chunk->z * CHUNK_D + z); - return Lightmap::extractNormalized(light); - } else { - return glm::vec4(0.0f); - } + light_t light = voxelsBuffer->pickLight( + chunk->x * CHUNK_W + x, y, chunk->z * CHUNK_D + z + ); + return light ? Lightmap::extractNormalized(light) : glm::vec4(0.0f); } glm::vec4 BlocksRenderer::pickLight(const glm::ivec3& coord) const { @@ -694,7 +678,9 @@ SortingMeshData BlocksRenderer::renderTranslucent( return sortingMesh; } -void BlocksRenderer::build(const Chunk* chunk, const VoxelsVolume& volume) { +void BlocksRenderer::build( + const Chunk* chunk, const VoxelsRenderVolume& volume +) { this->chunk = chunk; this->voxelsBuffer = &volume; if (voxelsBuffer->pickBlockId( @@ -707,16 +693,17 @@ void BlocksRenderer::build(const Chunk* chunk, const VoxelsVolume& volume) { 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; + beginEnds[variant.drawGroup][0] = i + 1; } beginEnds[variant.drawGroup][1] = i; } @@ -729,7 +716,11 @@ void BlocksRenderer::build(const Chunk* chunk, const VoxelsVolume& volume) { denseRender = false; densePass = false; - sortingMesh = renderTranslucent(voxels, beginEnds); + if (hasTranslucent) { + sortingMesh = renderTranslucent(voxels, beginEnds); + } else { + sortingMesh = {}; + } overflow = false; vertexCount = 0; @@ -775,7 +766,7 @@ ChunkMeshData BlocksRenderer::createMesh() { } ChunkMesh BlocksRenderer::render( - const Chunk* chunk, const VoxelsVolume& volume + const Chunk* chunk, const VoxelsRenderVolume& volume ) { build(chunk, volume); diff --git a/src/graphics/render/BlocksRenderer.hpp b/src/graphics/render/BlocksRenderer.hpp index 1eee81783..52eebeb26 100644 --- a/src/graphics/render/BlocksRenderer.hpp +++ b/src/graphics/render/BlocksRenderer.hpp @@ -17,7 +17,6 @@ class Content; class Block; class Chunk; class Chunks; -class VoxelsVolume; class ContentGfxCache; struct UVRegion; @@ -31,9 +30,9 @@ public: ); ~BlocksRenderer(); - void build(const Chunk* chunk, const VoxelsVolume& volume); + void build(const Chunk* chunk, const VoxelsRenderVolume& volume); ChunkMesh render( - const Chunk* chunk, const VoxelsVolume& volume + const Chunk* chunk, const VoxelsRenderVolume& volume ); ChunkMeshData createMesh(); @@ -58,7 +57,7 @@ private: bool densePass = false; bool denseRender = false; const Chunk* chunk = nullptr; - const VoxelsVolume* voxelsBuffer = nullptr; + const VoxelsRenderVolume* voxelsBuffer = nullptr; const Block* const* blockDefsCache; const ContentGfxCache& cache; @@ -143,8 +142,6 @@ private: bool ao ); - bool isOpenForLight(int x, int y, int z) const; - // 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 { auto vox = voxelsBuffer->pickBlock( diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index b906b9b5e..31d44cf13 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -19,6 +19,8 @@ static debug::Logger logger("chunks-render"); size_t ChunksRenderer::visibleChunks = 0; +static constexpr inline size_t MAX_CHUNKS_ENQUEUED_IN_FRAME = 4; + class RendererWorker : public util::Worker { BlocksRenderer renderer; public: @@ -51,8 +53,7 @@ public: } }; -static util::ObjectsPool voxelsVolumesPool {}; -static inline const int VOXELS_BUFFER_PADDING = 2; +static util::ObjectsPool voxelsVolumesPool {}; ChunksRenderer::ChunksRenderer( const Level* level, @@ -69,7 +70,7 @@ ChunksRenderer::ChunksRenderer( threadPool( "chunks-render-pool", [&]() { - return std::make_shared( + return std::make_unique( *level, cache, settings ); }, @@ -105,14 +106,10 @@ ChunksRenderer::ChunksRenderer( ChunksRenderer::~ChunksRenderer() = default; -std::shared_ptr ChunksRenderer::prepareVoxelsVolume( +std::shared_ptr ChunksRenderer::prepareVoxelsVolume( const Chunk& chunk ) { - auto voxelsBuffer = voxelsVolumesPool.create( - CHUNK_W + VOXELS_BUFFER_PADDING * 2, - CHUNK_H, - CHUNK_D + VOXELS_BUFFER_PADDING * 2 - ); + auto voxelsBuffer = voxelsVolumesPool.create(); voxelsBuffer->setPosition( chunk.x * CHUNK_W - VOXELS_BUFFER_PADDING, 0, chunk.z * CHUNK_D - VOXELS_BUFFER_PADDING @@ -123,8 +120,10 @@ std::shared_ptr ChunksRenderer::prepareVoxelsVolume( return voxelsBuffer; } +#include "util/timeutil.hpp" + const Mesh* ChunksRenderer::render( - const std::shared_ptr& chunk, bool important + const std::shared_ptr& chunk, bool important, bool lowPriority ) { glm::ivec2 key(chunk->x, chunk->z); chunk->flags.modified = false; @@ -136,16 +135,16 @@ const Mesh* ChunksRenderer::render( std::move(mesh.mesh), std::move(mesh.sortingMeshData), nullptr}; return meshes[key].mesh.get(); } - if (inwork.find(key) != inwork.end()) { + if (inwork.find(key) != inwork.end() || + ((inwork.size() >= threadPool.getWorkTotal() || + enqueuedInFrame >= MAX_CHUNKS_ENQUEUED_IN_FRAME) && + lowPriority)) { return nullptr; } + enqueuedInFrame++; auto voxelsBuffer = prepareVoxelsVolume(*chunk); - inwork[key] = true; - chunks.getVoxels( - *voxelsBuffer, settings.graphics.backlight.get(), chunk->top + 1 - ); - threadPool.enqueueJob({chunk, std::move(voxelsBuffer)}); + inwork[key] = true; return nullptr; } @@ -163,20 +162,21 @@ void ChunksRenderer::clear() { } const Mesh* ChunksRenderer::getOrRender( - const std::shared_ptr& chunk, bool important + 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); + return render(chunk, important, lowPriority); } if (chunk->flags.modified && chunk->flags.lighted) { - render(chunk, important); + render(chunk, important, lowPriority); } return found->second.mesh.get(); } void ChunksRenderer::update() { threadPool.pullResults(); + enqueuedInFrame = 0; } const Mesh* ChunksRenderer::retrieveChunk( @@ -202,7 +202,11 @@ const Mesh* ChunksRenderer::retrieveChunk( (chunk->z + 0.5f) * CHUNK_D ) ); - auto mesh = getOrRender(chunk, distance < CHUNK_W * 1.5f); + auto mesh = getOrRender( + chunk, + distance < CHUNK_W * 1.5f, + distance > CHUNK_W * settings.chunks.loadDistance.get() * 0.5 + ); if (mesh == nullptr) { return nullptr; } diff --git a/src/graphics/render/ChunksRenderer.hpp b/src/graphics/render/ChunksRenderer.hpp index 9794a8405..396a29c12 100644 --- a/src/graphics/render/ChunksRenderer.hpp +++ b/src/graphics/render/ChunksRenderer.hpp @@ -21,7 +21,6 @@ class Chunks; class Frustum; class BlocksRenderer; class ContentGfxCache; -class VoxelsVolume; struct EngineSettings; struct ChunksSortEntry { @@ -41,7 +40,7 @@ struct RendererResult { struct RendererJob { std::shared_ptr chunk; - std::shared_ptr volume; + std::shared_ptr volume; }; class ChunksRenderer { @@ -58,7 +57,8 @@ class ChunksRenderer { const Mesh* retrieveChunk( size_t index, const Camera& camera, bool culling ); - std::shared_ptr prepareVoxelsVolume(const Chunk& chunk); + std::shared_ptr prepareVoxelsVolume(const Chunk& chunk); + size_t enqueuedInFrame = 0; public: ChunksRenderer( const Level* level, @@ -71,13 +71,13 @@ public: virtual ~ChunksRenderer(); const Mesh* render( - const std::shared_ptr& chunk, bool important + const std::shared_ptr& chunk, bool important, bool lowPriority ); void unload(const Chunk* chunk); void clear(); const Mesh* getOrRender( - const std::shared_ptr& chunk, bool important + const std::shared_ptr& chunk, bool important, bool lowPriority ); void drawShadowsPass( diff --git a/src/graphics/render/commons.hpp b/src/graphics/render/commons.hpp index 5118f9c7d..41b352bac 100644 --- a/src/graphics/render/commons.hpp +++ b/src/graphics/render/commons.hpp @@ -1,5 +1,6 @@ #pragma once +#include "constants.hpp" #include "graphics/core/MeshData.hpp" #include "util/Buffer.hpp" @@ -51,3 +52,12 @@ struct ChunkMesh { SortingMeshData sortingMeshData; std::unique_ptr > sortedMesh; }; + +inline constexpr int VOXELS_BUFFER_PADDING = 2; + +template class StaticVoxelsVolume; + +using VoxelsRenderVolume = StaticVoxelsVolume< + CHUNK_W + VOXELS_BUFFER_PADDING * 2, + CHUNK_H, + CHUNK_D + VOXELS_BUFFER_PADDING * 2>; diff --git a/src/util/ObjectsPool.hpp b/src/util/ObjectsPool.hpp index 25f633ab1..45923a2c5 100644 --- a/src/util/ObjectsPool.hpp +++ b/src/util/ObjectsPool.hpp @@ -50,6 +50,10 @@ namespace util { size_t countTotal() const { return objects.size(); } + + size_t countFree() const { + return freeObjects.size(); + } private: std::vector> objects; std::queue freeObjects; diff --git a/src/util/ThreadPool.hpp b/src/util/ThreadPool.hpp index 4369d2856..198854a7d 100644 --- a/src/util/ThreadPool.hpp +++ b/src/util/ThreadPool.hpp @@ -52,7 +52,7 @@ namespace util { bool standaloneResults = true; bool stopOnFail = true; - void threadLoop(int index, std::shared_ptr> worker) { + void threadLoop(int index, std::unique_ptr> worker) { std::condition_variable variable; std::mutex mutex; bool locked = false; @@ -76,7 +76,7 @@ namespace util { { std::lock_guard lock(resultsMutex); results.push(ThreadPoolResult { - job, variable, index, locked, result}); + job, variable, index, locked, std::move(result)}); if (!standaloneResults) { locked = true; } @@ -115,7 +115,7 @@ namespace util { /// unlimited, -2 is half of auto count, -4 is quarter. ThreadPool( std::string name, - supplier>> workersSupplier, + supplier>> workersSupplier, consumer resultConsumer, int maxWorkers=UNLIMITED ) @@ -236,7 +236,7 @@ namespace util { return resultsProcessed; } - void enqueueJob(T job) { + void enqueueJob(T&& job) { { std::lock_guard lock(jobsMutex); jobs.push(std::move(job)); diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index a4b693829..d835b26ba 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -335,26 +335,106 @@ bool Chunks::putChunk(const std::shared_ptr& chunk) { return false; } -// reduce nesting on next modification -// 25.06.2024: not now -// 11.11.2024: not now -// 12.12.2025: not now -void Chunks::getVoxels(VoxelsVolume& volume, bool backlight, int top) const { - voxel* voxels = volume.getVoxels(); - light_t* lights = volume.getLights(); - int x = volume.getX(); - int y = volume.getY(); - int z = volume.getZ(); +static void fill_with_void( + voxel* voxels, + light_t* lights, + const glm::ivec3& pos, + const glm::ivec3& size, + int cx, + int cz +) { + for (int ly = pos.y; ly < pos.y + size.y; ly++) { + for (int lz = std::max(pos.z, cz * CHUNK_D); + lz < std::min(pos.z + size.z, (cz + 1) * CHUNK_D); + lz++) { + for (int lx = std::max(pos.x, cx * CHUNK_W); + lx < std::min(pos.x + size.x, (cx + 1) * CHUNK_W); + lx++) { + uint idx = vox_index( + lx - pos.x, ly - pos.y, lz - pos.z, size.x, size.z + ); + voxels[idx].id = BLOCK_VOID; + lights[idx] = 0; + } + } + } +} - int w = volume.getW(); - int h = std::min(volume.getH(), top); - int d = volume.getD(); +static inline light_t apply_backlight(light_t light) { + return Lightmap::combine( + std::min(15, Lightmap::extract(light, 0) + 1), + std::min(15, Lightmap::extract(light, 1) + 1), + std::min(15, Lightmap::extract(light, 2) + 1), + std::min(15, static_cast(Lightmap::extract(light, 3))) + ); +} - int scx = floordiv(x); - int scz = floordiv(z); +#include "util/timeutil.hpp" - int ecx = floordiv(x + w); - int ecz = floordiv(z + d); +// ugly +static inline void sample_chunk( + const decltype(ContentIndices::blocks)& defs, + const Chunk& chunk, + voxel* voxels, + light_t* lights, + const glm::ivec3& pos, + const glm::ivec3& size, + int cx, + int cz, + bool backlight +) { + const auto cvoxels = chunk.voxels; + const auto clights = chunk.lightmap ? chunk.lightmap->getLights() : nullptr; + for (int ly = pos.y; ly < pos.y + size.y; ly++) { + for (int lz = std::max(pos.z, cz * CHUNK_D); + lz < std::min(pos.z + size.z, (cz + 1) * CHUNK_D); + lz++) { + for (int lx = std::max(pos.x, cx * CHUNK_W); + lx < std::min(pos.x + size.x, (cx + 1) * CHUNK_W); + lx++) { + uint vidx = vox_index( + lx - pos.x, ly - pos.y, lz - pos.z, size.x, size.z + ); + uint cidx = vox_index( + lx - cx * CHUNK_W, + ly, + lz - cz * CHUNK_D, + CHUNK_W, + CHUNK_D + ); + auto& vox = voxels[vidx]; + vox = cvoxels[cidx]; + light_t light = clights ? clights[cidx] + : Lightmap::SUN_LIGHT_ONLY; + // todo: move to the BlocksRenderer + if (backlight) { + const auto block = defs.get(vox.id); + if (block && block->lightPassing) { + light = apply_backlight(light); + } + } + lights[vidx] = light; + } + } + } +} + +void Chunks::getVoxels( + voxel* voxels, + light_t* lights, + const glm::ivec3& pos, + const glm::ivec3& size, + bool backlight, + int top +) const { + timeutil::ScopeLogTimer log(111); + int h = std::min(size.y, top); + + int scx = floordiv(pos.x); + int scz = floordiv(pos.z); + + int ecx = floordiv(pos.x + size.x); + int ecz = floordiv(pos.z + size.z); int cw = ecx - scx + 1; int cd = ecz - scz + 1; @@ -364,67 +444,37 @@ void Chunks::getVoxels(VoxelsVolume& volume, bool backlight, int top) const { for (int cx = scx; cx < scx + cw; cx++) { const auto chunk = getChunk(cx, cz); if (chunk == nullptr) { - // no chunk loaded -> filling with BLOCK_VOID - for (int ly = y; ly < y + h; ly++) { - for (int lz = std::max(z, cz * CHUNK_D); - lz < std::min(z + d, (cz + 1) * CHUNK_D); - lz++) { - for (int lx = std::max(x, cx * CHUNK_W); - lx < std::min(x + w, (cx + 1) * CHUNK_W); - lx++) { - uint idx = vox_index(lx - x, ly - y, lz - z, w, d); - voxels[idx].id = BLOCK_VOID; - lights[idx] = 0; - } - } - } - } else { - const voxel* cvoxels = chunk->voxels; - const light_t* clights = - chunk->lightmap ? chunk->lightmap->getLights() : nullptr; - for (int ly = y; ly < y + h; ly++) { - for (int lz = std::max(z, cz * CHUNK_D); - lz < std::min(z + d, (cz + 1) * CHUNK_D); - lz++) { - for (int lx = std::max(x, cx * CHUNK_W); - lx < std::min(x + w, (cx + 1) * CHUNK_W); - lx++) { - uint vidx = vox_index(lx - x, ly - y, lz - z, w, d); - uint cidx = vox_index( - lx - cx * CHUNK_W, - ly, - lz - cz * CHUNK_D, - CHUNK_W, - CHUNK_D - ); - voxels[vidx] = cvoxels[cidx]; - light_t light = clights ? clights[cidx] - : Lightmap::SUN_LIGHT_ONLY; - if (backlight) { - const auto block = - indices.blocks.get(voxels[vidx].id); - if (block && block->lightPassing) { - light = Lightmap::combine( - std::min(15, - Lightmap::extract(light, 0) + 1), - std::min(15, - Lightmap::extract(light, 1) + 1), - std::min(15, - Lightmap::extract(light, 2) + 1), - std::min(15, - static_cast(Lightmap::extract(light, 3))) - ); - } - } - lights[vidx] = light; - } - } - } + fill_with_void( + voxels, lights, pos, {size.x, h, size.z}, cx, cz + ); + continue; } + sample_chunk( + indices.blocks, + *chunk, + voxels, + lights, + pos, + {size.x, h, size.z}, + cx, + cz, + backlight + ); } } } +void Chunks::getVoxels(VoxelsVolume& volume, bool backlight, int top) const { + getVoxels( + volume.getVoxels(), + volume.getLights(), + {volume.getX(), volume.getY(), volume.getZ()}, + {volume.getW(), volume.getH(), volume.getD()}, + backlight, + top + ); +} + void Chunks::saveAndClear() { areaMap.clear(); } diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index e185ce584..851ffb599 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -23,6 +23,8 @@ class LevelEvents; class Block; class VoxelsVolume; +template class StaticVoxelsVolume; + /// Player-centred chunks matrix class Chunks { LevelEvents* events; @@ -128,7 +130,34 @@ public: bool isReplaceableBlock(int32_t x, int32_t y, int32_t z); bool isObstacleBlock(int32_t x, int32_t y, int32_t z); - void getVoxels(VoxelsVolume& volume, bool backlight = false, int top = CHUNK_H) const; + void getVoxels( + VoxelsVolume& volume, bool backlight = false, int top = CHUNK_H + ) const; + + template + void getVoxels( + StaticVoxelsVolume& volume, + bool backlight = false, + int top = CHUNK_H + ) const { + getVoxels( + volume.getVoxels(), + volume.getLights(), + {volume.getX(), volume.getY(), volume.getZ()}, + {w, h, d}, + backlight, + top + ); + } + + void getVoxels( + voxel* voxels, + light_t* lights, + const glm::ivec3& pos, + const glm::ivec3& size, + bool backlight, + int top + ) const; void setCenter(int32_t x, int32_t z); void resize(uint32_t newW, uint32_t newD); diff --git a/src/voxels/VoxelsVolume.cpp b/src/voxels/VoxelsVolume.cpp deleted file mode 100644 index d74f69eef..000000000 --- a/src/voxels/VoxelsVolume.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "VoxelsVolume.hpp" - -VoxelsVolume::VoxelsVolume(int x, int y, int z, int w, int h, int d) - : x(x), - y(y), - z(z), - w(w), - h(h), - d(d), - voxels(std::make_unique(w * h * d)), - lights(std::make_unique(w * h * d)) { - for (int i = 0; i < w * h * d; i++) { - voxels[i].id = BLOCK_VOID; - } -} - -VoxelsVolume::VoxelsVolume(int w, int h, int d) - : VoxelsVolume(0, 0, 0, w, h, d) { -} - -VoxelsVolume::~VoxelsVolume() { -} - -void VoxelsVolume::setPosition(int x, int y, int z) { - this->x = x; - this->y = y; - this->z = z; -} diff --git a/src/voxels/VoxelsVolume.hpp b/src/voxels/VoxelsVolume.hpp index 72afc0786..bf57ae9e9 100644 --- a/src/voxels/VoxelsVolume.hpp +++ b/src/voxels/VoxelsVolume.hpp @@ -4,17 +4,25 @@ #include "typedefs.hpp" #include "voxel.hpp" -class VoxelsVolume { - int x, y, z; - int w, h, d; - std::unique_ptr voxels; - std::unique_ptr lights; -public: - VoxelsVolume(int w, int h, int d); - VoxelsVolume(int x, int y, int z, int w, int h, int d); - virtual ~VoxelsVolume(); +#include - void setPosition(int x, int y, int z); +class VoxelsVolume { +public: + static inline constexpr size_t MAX_VOXELS = CHUNK_VOL * 2; + + VoxelsVolume(int w, int h, int d) + : VoxelsVolume(0, 0, 0, w, h, d) {} + + VoxelsVolume(int x, int y, int z, int w, int h, int d) + : x(x), y(y), z(z), w(w), h(h), d(d) { + std::memset(voxels.get(), 0xFF, sizeof(voxel) * w * h * d); + } + + void setPosition(int x, int y, int z) { + this->x = x; + this->y = y; + this->z = z; + } int getX() const { return x; @@ -40,11 +48,11 @@ public: return d; } - voxel* getVoxels() const { + voxel* getVoxels() { return voxels.get(); } - light_t* getLights() const { + light_t* getLights() { return lights.get(); } @@ -72,4 +80,83 @@ public: } return lights[vox_index(bx - x, by - y, bz - z, w, d)]; } +private: + int x, y, z; + int w, h, d; + std::unique_ptr voxels; + std::unique_ptr lights; }; + + +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; + + StaticVoxelsVolume() + : StaticVoxelsVolume(0, 0, 0) {} + + StaticVoxelsVolume(int x, int y, int z) + : x(x), y(y), z(z) { + std::memset(voxels, 0xFF, sizeof(voxel) * size); + } + + void setPosition(int x, int y, int z) { + this->x = x; + this->y = y; + this->z = z; + } + + int getX() const { + return x; + } + + int getY() const { + return y; + } + + int getZ() const { + return z; + } + + voxel* getVoxels() { + return voxels; + } + + light_t* getLights() { + return lights; + } + + inline 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; + } + return voxels[vox_index(bx - x, by - y, bz - z, w, d)].id; + } + + + inline 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, {}}; + } + return voxels[vox_index(bx - x, by - y, bz - z, w, d)]; + } + + inline 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; + } + return lights[vox_index(bx - x, by - y, bz - z, w, d)]; + } +private: + int x, y, z; + voxel voxels[size]; + light_t lights[size]; +}; + diff --git a/src/world/files/WorldConverter.cpp b/src/world/files/WorldConverter.cpp index 431d4c41c..2f7716d3f 100644 --- a/src/world/files/WorldConverter.cpp +++ b/src/world/files/WorldConverter.cpp @@ -162,7 +162,7 @@ std::shared_ptr WorldConverter::startTask( } auto pool = std::make_shared>( "converter-pool", - [=]() { return std::make_shared(converter); }, + [=]() { return std::make_unique(converter); }, [=](int&&) {} ); auto& converterTasks = converter->tasks; From 99757103fce836b0440b0ea7972a324e0c9ce699 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 29 Dec 2025 21:39:26 +0300 Subject: [PATCH 09/42] cleanup --- src/graphics/render/ChunksRenderer.cpp | 3 +-- src/voxels/Chunks.cpp | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index 31d44cf13..717064fb2 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -120,11 +120,10 @@ std::shared_ptr ChunksRenderer::prepareVoxelsVolume( return voxelsBuffer; } -#include "util/timeutil.hpp" - const Mesh* ChunksRenderer::render( const std::shared_ptr& chunk, bool important, bool lowPriority ) { + important = true; glm::ivec2 key(chunk->x, chunk->z); chunk->flags.modified = false; if (important) { diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index d835b26ba..4b73f1abe 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -369,8 +369,6 @@ static inline light_t apply_backlight(light_t light) { ); } -#include "util/timeutil.hpp" - // ugly static inline void sample_chunk( const decltype(ContentIndices::blocks)& defs, @@ -427,7 +425,6 @@ void Chunks::getVoxels( bool backlight, int top ) const { - timeutil::ScopeLogTimer log(111); int h = std::min(size.y, top); int scx = floordiv(pos.x); From c323b1ad155439edd103997904fc053d2fd4d09c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 29 Dec 2025 23:32:16 +0300 Subject: [PATCH 10/42] fixes --- src/graphics/render/BlocksRenderer.hpp | 2 +- src/graphics/render/ChunksRenderer.cpp | 3 +-- src/voxels/Block.hpp | 2 +- src/voxels/VoxelsVolume.hpp | 33 ++++++++++++++++---------- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/graphics/render/BlocksRenderer.hpp b/src/graphics/render/BlocksRenderer.hpp index 52eebeb26..8af7fab7a 100644 --- a/src/graphics/render/BlocksRenderer.hpp +++ b/src/graphics/render/BlocksRenderer.hpp @@ -144,7 +144,7 @@ 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 { - auto vox = voxelsBuffer->pickBlock( + const auto& vox = voxelsBuffer->pickBlock( chunk->x * CHUNK_W + pos.x, pos.y, chunk->z * CHUNK_D + pos.z ); if (vox.id == BLOCK_VOID) { diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index 717064fb2..48fdc516f 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -123,7 +123,6 @@ std::shared_ptr ChunksRenderer::prepareVoxelsVolume( const Mesh* ChunksRenderer::render( const std::shared_ptr& chunk, bool important, bool lowPriority ) { - important = true; glm::ivec2 key(chunk->x, chunk->z); chunk->flags.modified = false; if (important) { @@ -135,7 +134,7 @@ const Mesh* ChunksRenderer::render( return meshes[key].mesh.get(); } if (inwork.find(key) != inwork.end() || - ((inwork.size() >= threadPool.getWorkTotal() || + ((inwork.size() >= threadPool.getWorkersCount() || enqueuedInFrame >= MAX_CHUNKS_ENQUEUED_IN_FRAME) && lowPriority)) { return nullptr; diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index 29eda2494..c2b567427 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -123,7 +123,7 @@ VC_ENUM_METADATA(BlockModelType) {"custom", BlockModelType::CUSTOM}, VC_ENUM_END -enum class CullingMode { +enum class CullingMode : uint8_t { DEFAULT, OPTIONAL, DISABLED, diff --git a/src/voxels/VoxelsVolume.hpp b/src/voxels/VoxelsVolume.hpp index bf57ae9e9..14ac0a4f4 100644 --- a/src/voxels/VoxelsVolume.hpp +++ b/src/voxels/VoxelsVolume.hpp @@ -130,29 +130,36 @@ public: return lights; } - inline 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) { + inline blockid_t pickBlockId(uint bx, uint by, uint bz) const { + bx -= x; + by -= y; + bz -= z; + if (bx >= w || by >= h || bz >= d) { return BLOCK_VOID; } - return voxels[vox_index(bx - x, by - y, bz - z, w, d)].id; + return voxels[vox_index(bx, by, bz, w, d)].id; } - inline 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, {}}; + inline const voxel& pickBlock(uint bx, uint by, uint bz) const { + bx -= x; + by -= y; + bz -= z; + if (bx >= w || by >= h || bz >= d) { + static voxel voidVoxel {BLOCK_VOID, {}}; + return voidVoxel; } - return voxels[vox_index(bx - x, by - y, bz - z, w, d)]; + return voxels[vox_index(bx, by, bz, w, d)]; } - inline 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) { + inline light_t pickLight(uint bx, uint by, uint bz) const { + bx -= x; + by -= y; + bz -= z; + if (bx >= w || by >= h || bz >= d) { return 0; } - return lights[vox_index(bx - x, by - y, bz - z, w, d)]; + return lights[vox_index(bx, by, bz, w, d)]; } private: int x, y, z; From 8b5261f4f14ee31894dcf5125a4836ff140de8a9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 18 Jan 2026 23:35:18 +0300 Subject: [PATCH 11/42] fix fatal error when skeleton is missing --- src/logic/scripting/lua/libs/lib__skeleton.cpp | 6 ++++++ src/objects/Entities.cpp | 13 +++++++++---- src/objects/Entity.cpp | 2 +- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/logic/scripting/lua/libs/lib__skeleton.cpp b/src/logic/scripting/lua/libs/lib__skeleton.cpp index be42c356e..2fb5d4fa0 100644 --- a/src/logic/scripting/lua/libs/lib__skeleton.cpp +++ b/src/logic/scripting/lua/libs/lib__skeleton.cpp @@ -36,6 +36,9 @@ static rigging::Skeleton* get_skeleton(lua::State* L) { static int l_get_model(lua::State* L) { if (auto skeleton = get_skeleton(L)) { + if (skeleton->config == nullptr) { + return 0; + } auto& rigConfig = *skeleton->config; auto index = index_range_check(*skeleton, lua::tointeger(L, 2)); const auto& modelOverride = skeleton->modelOverrides[index]; @@ -96,6 +99,9 @@ static int l_set_texture(lua::State* L) { static int l_index(lua::State* L) { if (auto skeleton = get_skeleton(L)) { + if (skeleton->config == nullptr) { + return 0; + } if (auto bone = skeleton->config->find(lua::require_string(L, 2))) { return lua::pushinteger(L, bone->getIndex()); } diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 179a39fd6..e1040776d 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -147,7 +147,7 @@ void Entities::loadEntity(const dv::value& map, Entity entity) { if (map.has(COMP_TRANSFORM)) { transform.deserialize(map[COMP_TRANSFORM]); } - if (skeleton == nullptr) { + if (skeleton == nullptr || skeleton->config == nullptr) { return; } std::string skeletonName = skeleton->config->getName(); @@ -397,6 +397,9 @@ void Entities::renderDebug( ctx.setLineWidth(2); for (auto [entity, transform, skeleton] : view.each()) { auto config = skeleton.config; + if (config == nullptr) { + continue; + } const auto& pos = transform.pos; const auto& size = transform.size; if (frustum && !frustum->isBoxVisible(pos - size, pos + size)) { @@ -434,9 +437,11 @@ void Entities::render( } const auto* rigConfig = skeleton.config; - rigConfig->render( - assets, batch, skeleton, transform.rot, pos, size - ); + if (rigConfig) { + rigConfig->render( + assets, batch, skeleton, transform.rot, pos, size + ); + } } } diff --git a/src/objects/Entity.cpp b/src/objects/Entity.cpp index 5fbfe1d99..e8c276dd2 100644 --- a/src/objects/Entity.cpp +++ b/src/objects/Entity.cpp @@ -64,7 +64,7 @@ dv::value Entity::serialize() const { root[COMP_RIGIDBODY] = rigidbody.serialize(def.save.body.velocity, def.save.body.settings); - if (skeleton != nullptr) { + if (skeleton != nullptr && skeleton->config != nullptr) { if (skeleton->config->getName() != def.skeletonName) { root["skeleton-name"] = skeleton->config->getName(); } From 3055621894618f764a3e760bf9b906e86c7987dc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 2 Feb 2026 18:35:44 +0300 Subject: [PATCH 12/42] improve sky lighting --- res/shaders/entity.glslv | 4 +++- res/shaders/lib/constants.glsl | 4 ++-- res/shaders/lib/sky.glsl | 5 +++-- res/shaders/main.glslv | 4 +++- res/shaders/translucent.glslv | 4 +++- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/res/shaders/entity.glslv b/res/shaders/entity.glslv index c337ddb2d..d50cd0075 100644 --- a/res/shaders/entity.glslv +++ b/res/shaders/entity.glslv @@ -13,6 +13,8 @@ layout (location = 4) in vec4 v_normal; out vec4 a_color; +uniform float u_dayTime; + void main() { a_modelpos = u_model * vec4(v_position, 1.0); vec3 pos3d = a_modelpos.xyz - u_cameraPos; @@ -26,7 +28,7 @@ void main() { a_texCoord = v_texCoord; a_dir = a_modelpos.xyz - u_cameraPos; - vec3 skyLightColor = pick_sky_color(u_skybox); + vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime); a_color.rgb = max(a_color.rgb, skyLightColor.rgb * v_light.a) * v_color; a_color.a = u_opacity; diff --git a/res/shaders/lib/constants.glsl b/res/shaders/lib/constants.glsl index 74f295458..1c2f4062b 100644 --- a/res/shaders/lib/constants.glsl +++ b/res/shaders/lib/constants.glsl @@ -8,8 +8,8 @@ #define CURVATURE_FACTOR 0.002 // lighting -#define SKY_LIGHT_MUL 2.9 -#define SKY_LIGHT_TINT (vec3(1.0, 0.95, 0.9) * 2.0) +#define SKY_LIGHT_MUL 2.5 +#define SKY_LIGHT_TINT (vec3(1.0, 0.75, 0.6) * 2.0) #define MIN_SKY_LIGHT vec3(0.2, 0.25, 0.33) // fog diff --git a/res/shaders/lib/sky.glsl b/res/shaders/lib/sky.glsl index ce3604f5f..97203286b 100644 --- a/res/shaders/lib/sky.glsl +++ b/res/shaders/lib/sky.glsl @@ -3,8 +3,9 @@ #include -vec3 pick_sky_color(samplerCube cubemap) { - vec3 skyLightColor = texture(cubemap, vec3(0.4f, 0.05f, 0.4f)).rgb; +vec3 pick_sky_color(samplerCube cubemap, float dayTime) { + float elevation = sin(dayTime * PI2) * 0.2; + vec3 skyLightColor = texture(cubemap, vec3(0.0f, elevation, -0.4f)).rgb; skyLightColor *= SKY_LIGHT_TINT; skyLightColor = min(vec3(1.0f), skyLightColor * SKY_LIGHT_MUL); skyLightColor = max(MIN_SKY_LIGHT, skyLightColor); diff --git a/res/shaders/main.glslv b/res/shaders/main.glslv index 3d7220297..8cc7a7637 100644 --- a/res/shaders/main.glslv +++ b/res/shaders/main.glslv @@ -10,6 +10,8 @@ layout (location = 3) in vec4 v_normal; #include #include +uniform float u_dayTime; + out vec4 a_torchLight; void main() { @@ -25,7 +27,7 @@ void main() { a_texCoord = v_texCoord; a_dir = a_modelpos.xyz - u_cameraPos; - vec3 skyLightColor = pick_sky_color(u_skybox); + vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime); a_skyLight = skyLightColor.rgb*v_light.a; mat4 viewmodel = u_view * u_model; diff --git a/res/shaders/translucent.glslv b/res/shaders/translucent.glslv index 8c429e97b..16cc8c4e3 100644 --- a/res/shaders/translucent.glslv +++ b/res/shaders/translucent.glslv @@ -12,6 +12,8 @@ layout (location = 3) in vec4 v_normal; out vec4 a_torchLight; +uniform float u_dayTime; + void main() { a_modelpos = u_model * vec4(v_position, 1.0f); vec3 pos3d = a_modelpos.xyz - u_cameraPos; @@ -25,7 +27,7 @@ void main() { a_texCoord = v_texCoord; a_dir = a_modelpos.xyz - u_cameraPos; - vec3 skyLightColor = pick_sky_color(u_skybox); + vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime); a_skyLight = skyLightColor.rgb*v_light.a; mat4 viewmodel = u_view * u_model; From 5380bcf0133e6dba96a5bb9dbcb339f3bfd0f418 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 2 Feb 2026 22:40:54 +0300 Subject: [PATCH 13/42] refactor world renderer a bit --- src/frontend/screens/LevelScreen.cpp | 10 +- src/graphics/render/ChunksRenderer.cpp | 53 ++++-- src/graphics/render/ChunksRenderer.hpp | 4 +- src/graphics/render/HandsRenderer.cpp | 4 +- src/graphics/render/HandsRenderer.hpp | 2 +- src/graphics/render/ParticlesRenderer.cpp | 169 +++++++++++------- src/graphics/render/ParticlesRenderer.hpp | 8 +- src/graphics/render/PrecipitationRenderer.cpp | 8 +- src/graphics/render/PrecipitationRenderer.hpp | 4 +- src/graphics/render/WorldRenderer.cpp | 116 ++++++------ src/graphics/render/WorldRenderer.hpp | 62 +++---- src/objects/Entities.cpp | 18 +- src/objects/Entities.hpp | 2 - 13 files changed, 257 insertions(+), 203 deletions(-) diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 02119b9ad..e910ace67 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -184,7 +184,7 @@ void LevelScreen::saveWorldPreview() { static_cast(previewSize)} ); - renderer->renderFrame(ctx, camera, false, true, 0.0f, *postProcessing); + renderer->renderFrame(ctx, camera, false, *postProcessing); auto image = postProcessing->toImage(); image->flipY(); imageio::write("world:preview.png", image.get()); @@ -271,9 +271,11 @@ void LevelScreen::draw(float delta) { if (!hud->isPause()) { scripting::on_entities_render(engine.getTime().getDelta()); } - renderer->renderFrame( - ctx, *camera, hudVisible, hud->isPause(), delta, *postProcessing - ); + renderer->update(*camera, delta * !hud->isPause()); + renderer->renderFrame(ctx, *camera, hudVisible, *postProcessing); + if (!hud->isPause()) { + scripting::on_frontend_render(); + } if (hudVisible) { hud->draw(ctx); diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index 48fdc516f..2ba631b60 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -56,7 +56,7 @@ public: static util::ObjectsPool voxelsVolumesPool {}; ChunksRenderer::ChunksRenderer( - const Level* level, + const Level& level, const Chunks& chunks, const Assets& assets, const Frustum& frustum, @@ -71,7 +71,7 @@ ChunksRenderer::ChunksRenderer( "chunks-render-pool", [&]() { return std::make_unique( - *level, cache, settings + level, cache, settings ); }, [&](RendererResult&& result) { @@ -90,7 +90,7 @@ ChunksRenderer::ChunksRenderer( threadPool.setStopOnFail(false); renderer = std::make_unique( settings.graphics.chunkMaxVertices.get(), - level->content, cache, settings + level.content, cache, settings ); logger.info() << "created " << threadPool.getWorkersCount() << " workers"; logger.info() << "memory consumption is " @@ -120,17 +120,32 @@ std::shared_ptr ChunksRenderer::prepareVoxelsVolume( return voxelsBuffer; } +std::shared_ptr ChunksRenderer::prepareVoxelsVolumeDynamic( + const Chunk& chunk, int padding +) { + auto voxelsBuffer = std::make_unique( + CHUNK_W + padding * 2, CHUNK_H, CHUNK_D + padding * 2 + ); + voxelsBuffer->setPosition( + chunk.x * CHUNK_W - padding, 0, + chunk.z * CHUNK_D - padding + ); + chunks.getVoxels( + *voxelsBuffer, settings.graphics.backlight.get(), chunk.top + 3 + ); + return voxelsBuffer; +} + const Mesh* ChunksRenderer::render( const std::shared_ptr& chunk, bool important, bool lowPriority ) { glm::ivec2 key(chunk->x, chunk->z); chunk->flags.modified = false; - if (important) { + if (!important) { + ChunkMesh mesh {}; auto voxelsBuffer = prepareVoxelsVolume(*chunk); - - auto mesh = renderer->render(chunk.get(), *voxelsBuffer); - meshes[key] = ChunkMesh { - std::move(mesh.mesh), std::move(mesh.sortingMeshData), nullptr}; + mesh = renderer->render(chunk.get(), *voxelsBuffer); + meshes[key] = std::move(mesh); return meshes[key].mesh.get(); } if (inwork.find(key) != inwork.end() || @@ -202,7 +217,7 @@ const Mesh* ChunksRenderer::retrieveChunk( ); auto mesh = getOrRender( chunk, - distance < CHUNK_W * 1.5f, + distance < CHUNK_W * 1.5f * 10.0f, distance > CHUNK_W * settings.chunks.loadDistance.get() * 0.5 ); if (mesh == nullptr) { @@ -309,17 +324,17 @@ void ChunksRenderer::drawChunks( 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) { - 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), - (coord + glm::vec3(CHUNK_W * 0.5f, 0.0f, CHUNK_D * 0.5f))) < denseDistance2); - visibleChunks++; + if (mesh == nullptr) { + 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), + (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 396a29c12..483313379 100644 --- a/src/graphics/render/ChunksRenderer.hpp +++ b/src/graphics/render/ChunksRenderer.hpp @@ -58,10 +58,12 @@ class ChunksRenderer { size_t index, const Camera& camera, bool culling ); std::shared_ptr prepareVoxelsVolume(const Chunk& chunk); + std::shared_ptr prepareVoxelsVolumeDynamic(const Chunk& chunk, int padding); + size_t enqueuedInFrame = 0; public: ChunksRenderer( - const Level* level, + const Level& level, const Chunks& chunks, const Assets& assets, const Frustum& frustum, diff --git a/src/graphics/render/HandsRenderer.cpp b/src/graphics/render/HandsRenderer.cpp index 6b2913197..8e2f757b2 100644 --- a/src/graphics/render/HandsRenderer.cpp +++ b/src/graphics/render/HandsRenderer.cpp @@ -21,9 +21,7 @@ HandsRenderer::HandsRenderer( skeleton(std::move(skeleton)) { } -void HandsRenderer::renderHands( - const Camera& camera, float delta -) { +void HandsRenderer::render(const Camera& camera) { auto& skeleton = *this->skeleton; const auto& config = *skeleton.config; diff --git a/src/graphics/render/HandsRenderer.hpp b/src/graphics/render/HandsRenderer.hpp index 1aba08a21..60be84d8d 100644 --- a/src/graphics/render/HandsRenderer.hpp +++ b/src/graphics/render/HandsRenderer.hpp @@ -18,7 +18,7 @@ public: std::shared_ptr skeleton ); - void renderHands(const Camera& camera, float delta); + void render(const Camera& camera); private: const Assets& assets; ModelBatch& modelBatch; diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index 4788d0f46..8d92293d5 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -46,12 +46,7 @@ static inline void update_particle( particle.lifetime -= delta; } -void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { - const auto& right = camera.right; - const auto& up = camera.up; - - bool backlight = settings->backlight.get(); - +void ParticlesRenderer::updateParticles(float delta) { std::vector unusedTextures; for (auto& [texture, vec] : particles) { @@ -59,8 +54,6 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { unusedTextures.push_back(texture); continue; } - batch->setTexture(texture); - visibleParticles += vec.size(); auto iter = vec.begin(); @@ -87,59 +80,6 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { } } update_particle(particle, delta, chunks); - - float scale = 1.0f + ((particle.random ^ 2628172) % 1000) * - 0.001f * preset.sizeSpread; - - glm::vec4 light(1, 1, 1, 0); - if (preset.lighting) { - light = MainBatch::sampleLight( - particle.position, - chunks, - backlight - ); - auto size = glm::max(glm::vec3(0.5f), preset.size * scale); - for (int x = -1; x <= 1; x++) { - for (int y = -1; y <= 1; y++) { - for (int z = -1; z <= 1; z++) { - light = glm::max( - light, - MainBatch::sampleLight( - particle.position - - size * glm::vec3(x, y, z), - chunks, - backlight - ) - ); - } - } - } - light *= 0.9f + (particle.random % 100) * 0.001f; - } - - glm::vec3 localRight = right; - glm::vec3 localUp = preset.globalUpVector ? glm::vec3(0, 1, 0) : up; - float angle = particle.angle; - if (glm::abs(angle) >= 0.005f) { - glm::vec3 rotatedRight(glm::cos(angle), -glm::sin(angle), 0.0f); - glm::vec3 rotatedUp(glm::sin(angle), glm::cos(angle), 0.0f); - - localRight = right * rotatedRight.x + localUp * rotatedRight.y + - camera.front * rotatedRight.z; - localUp = right * rotatedUp.x + localUp * rotatedUp.y + - camera.front * rotatedUp.z; - } - batch->quad( - particle.position, - localRight, - localUp, - -camera.front, - preset.size * scale, - light, - glm::vec3(1.0f), - particle.region, - preset.lighting ? 0.0f : 1.0f - ); if (particle.lifetime <= 0.0f) { iter = vec.erase(iter); emitter.refCount--; @@ -148,19 +88,85 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { } } } - batch->flush(); + for (const auto& texture : unusedTextures) { particles.erase(texture); } } -void ParticlesRenderer::render(const Camera& camera, float delta) { - batch->begin(); - - aliveEmitters = emitters.size(); - visibleParticles = 0; +static inline glm::vec4 calc_lights( + Particle& particle, + ParticlesPreset& preset, + bool backlight, + float scale, + const Chunks& chunks +) { + auto light = MainBatch::sampleLight( + particle.position, + chunks, + backlight + ); + auto size = glm::max(glm::vec3(0.5f), preset.size * scale); + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + for (int z = -1; z <= 1; z++) { + light = glm::max( + light, + MainBatch::sampleLight( + particle.position - size * glm::vec3(x, y, z), + chunks, + backlight + ) + ); + } + } + } + light *= 0.9f + (particle.random % 100) * 0.001f; + return light; +} - renderParticles(camera, delta); +void ParticlesRenderer::renderParticle( + Particle& particle, const Camera& camera, bool backlight +) { + const auto& right = camera.right; + const auto& up = camera.up; + auto& emitter = *particle.emitter; + auto& preset = emitter.preset; + float scale = 1.0f + ((particle.random ^ 2628172) % 1000) * + 0.001f * preset.sizeSpread; + + glm::vec4 light(1, 1, 1, 0); + if (preset.lighting) { + light = calc_lights(particle, preset, backlight, scale, chunks); + } + + glm::vec3 localRight = right; + glm::vec3 localUp = preset.globalUpVector ? glm::vec3(0, 1, 0) : up; + float angle = particle.angle; + if (glm::abs(angle) >= 0.005f) { + glm::vec3 rotatedRight(glm::cos(angle), -glm::sin(angle), 0.0f); + glm::vec3 rotatedUp(glm::sin(angle), glm::cos(angle), 0.0f); + + localRight = right * rotatedRight.x + localUp * rotatedRight.y + + camera.front * rotatedRight.z; + localUp = right * rotatedUp.x + localUp * rotatedUp.y + + camera.front * rotatedUp.z; + } + batch->quad( + particle.position, + localRight, + localUp, + -camera.front, + preset.size * scale, + light, + glm::vec3(1.0f), + particle.region, + preset.lighting ? 0.0f : 1.0f + ); +} + +void ParticlesRenderer::update(const Camera& camera, float delta) { + updateParticles(delta); auto iter = emitters.begin(); while (iter != emitters.end()) { @@ -178,6 +184,33 @@ void ParticlesRenderer::render(const Camera& camera, float delta) { } } +void ParticlesRenderer::render(const Camera& camera) { + aliveEmitters = emitters.size(); + visibleParticles = 0; + + bool backlight = settings->backlight.get(); + + batch->begin(); + for (auto& [texture, vec] : particles) { + batch->setTexture(texture); + + auto iter = vec.begin(); + while (iter != vec.end()) { + auto& particle = *iter; + + renderParticle(particle, camera, backlight); + + if (particle.lifetime <= 0.0f) { + iter = vec.erase(iter); + particle.emitter->refCount--; + } else { + iter++; + } + } + } + batch->flush(); +} + Emitter* ParticlesRenderer::getEmitter(u64id_t id) const { const auto& found = emitters.find(id); if (found == emitters.end()) { diff --git a/src/graphics/render/ParticlesRenderer.hpp b/src/graphics/render/ParticlesRenderer.hpp index 506a3eac7..ed9c3f314 100644 --- a/src/graphics/render/ParticlesRenderer.hpp +++ b/src/graphics/render/ParticlesRenderer.hpp @@ -25,7 +25,10 @@ class ParticlesRenderer { std::unordered_map> emitters; u64id_t nextEmitter = 1; - void renderParticles(const Camera& camera, float delta); + void renderParticle( + Particle& particle, const Camera& camera, bool backlight + ); + void updateParticles(float delta); public: ParticlesRenderer( const Assets& assets, @@ -35,7 +38,8 @@ public: ); ~ParticlesRenderer(); - void render(const Camera& camera, float delta); + void update(const Camera& camera, float delta); + void render(const Camera& camera); u64id_t add(std::unique_ptr emitter); diff --git a/src/graphics/render/PrecipitationRenderer.cpp b/src/graphics/render/PrecipitationRenderer.cpp index 484425273..341d94f88 100644 --- a/src/graphics/render/PrecipitationRenderer.cpp +++ b/src/graphics/render/PrecipitationRenderer.cpp @@ -90,11 +90,13 @@ static UVRegion calc_uv( return {u1, v1, u1 + m * scale, v1 + FACE_SIZE.y * scale}; } -void PrecipitationRenderer::render( - const Camera& camera, float delta, const WeatherPreset& weather -) { +void PrecipitationRenderer::update(float delta) { timer += delta; +} +void PrecipitationRenderer::render( + const Camera& camera, const WeatherPreset& weather +) { const int radius = 6; const int depth = 12; diff --git a/src/graphics/render/PrecipitationRenderer.hpp b/src/graphics/render/PrecipitationRenderer.hpp index d482e90a0..889a58ff7 100644 --- a/src/graphics/render/PrecipitationRenderer.hpp +++ b/src/graphics/render/PrecipitationRenderer.hpp @@ -27,5 +27,7 @@ public: ~PrecipitationRenderer(); - void render(const Camera& camera, float delta, const WeatherPreset& weather); + void update(float delta); + + void render(const Camera& camera, const WeatherPreset& weather); }; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 7b505d868..e05e7191c 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -1,12 +1,5 @@ #include "WorldRenderer.hpp" -#include - -#include -#include -#include -#include - #include "assets/Assets.hpp" #include "assets/assets_util.hpp" #include "content/Content.hpp" @@ -18,7 +11,6 @@ #include "items/ItemDef.hpp" #include "items/ItemStack.hpp" #include "logic/PlayerController.hpp" -#include "logic/scripting/scripting_hud.hpp" #include "maths/FrustumCulling.hpp" #include "maths/voxmaths.hpp" #include "objects/Entities.hpp" @@ -59,6 +51,12 @@ #include "Emitter.hpp" #include "TextNote.hpp" +#include +#include +#include +#include +#include + using namespace advanced_pipeline; inline constexpr size_t BATCH3D_CAPACITY = 4096; @@ -81,7 +79,7 @@ WorldRenderer::WorldRenderer( MODEL_BATCH_CAPACITY, assets, *player.chunks, engine.getSettings() )), chunksRenderer(std::make_unique( - &level, + level, *player.chunks, assets, *frustumCulling, @@ -103,19 +101,18 @@ WorldRenderer::WorldRenderer( LevelEventType::CHUNK_HIDDEN, [this](LevelEventType, Chunk* chunk) { chunksRenderer->unload(chunk); } ); - auto assets = engine.getAssets(); skybox = std::make_unique( settings.graphics.skyboxResolution.get(), - assets->require("skybox_gen") + assets.require("skybox_gen") ); const auto& content = level.content; skeletons = std::make_unique(); - const auto& skeletonConfig = assets->require( + const auto& skeletonConfig = assets.require( content.getDefaults()["hand-skeleton"].asString() ); hands = std::make_unique( - *assets, *modelBatch, skeletons->createSkeleton("hand", &skeletonConfig) + assets, *modelBatch, skeletons->createSkeleton("hand", &skeletonConfig) ); shadowMapping = std::make_unique(level); debugLines = std::make_unique(level); @@ -179,8 +176,6 @@ void WorldRenderer::renderOpaque( const DrawContext& ctx, const Camera& camera, const EngineSettings& settings, - float delta, - bool pause, bool hudVisible ) { texts->render(ctx, camera, settings, hudVisible, false); @@ -203,13 +198,11 @@ void WorldRenderer::renderOpaque( assets, *modelBatch, culling ? frustumCulling.get() : nullptr, - delta, - pause, player.currentCamera.get() == player.fpCamera.get() ? player.getEntity() : 0 ); modelBatch->render(); - particles->render(camera, delta * !pause); + particles->render(camera); auto& shader = assets.require("main"); auto& linesShader = assets.require("lines"); @@ -222,10 +215,6 @@ void WorldRenderer::renderOpaque( if (hudVisible) { renderLines(camera, linesShader, ctx); } - - if (!pause) { - scripting::on_frontend_render(); - } skybox->unbind(); } @@ -275,45 +264,17 @@ void WorldRenderer::renderLines( } } -void WorldRenderer::renderFrame( - const DrawContext& pctx, - Camera& camera, - bool hudVisible, - bool pause, - float uiDelta, - PostProcessing& postProcessing -) { - // TODO: REFACTOR WHOLE RENDER ENGINE +void WorldRenderer::refreshSettings(Shader** shaders) { + const auto& graphics = engine.getSettings().graphics; + gbufferPipeline = graphics.advancedRender.get(); - auto projView = camera.getProjView(); - - float delta = uiDelta * !pause; - timer += delta; - weather.update(delta); - - auto world = level.getWorld(); - - const auto& vp = pctx.getViewport(); - camera.setAspectRatio(vp.x / static_cast(vp.y)); - - auto& mainShader = assets.require("main"); - auto& entityShader = assets.require("entity"); - auto& translucentShader = assets.require("translucent"); - auto& deferredShader = assets.require("deferred_lighting").getShader(); - const auto& settings = engine.getSettings(); - - Shader* affectedShaders[] { - &mainShader, &entityShader, &translucentShader, &deferredShader - }; - - gbufferPipeline = settings.graphics.advancedRender.get(); - int shadowsQuality = settings.graphics.shadowsQuality.get() * gbufferPipeline; + int shadowsQuality = graphics.shadowsQuality.get() * gbufferPipeline; shadowMapping->setQuality(shadowsQuality); - + CompileTimeShaderSettings currentSettings { gbufferPipeline, shadowsQuality != 0, - settings.graphics.ssao.get() && gbufferPipeline + graphics.ssao.get() && gbufferPipeline }; if ( prevCTShaderSettings.advancedRender != currentSettings.advancedRender || @@ -325,11 +286,44 @@ void WorldRenderer::renderFrame( if (currentSettings.ssao) defines.emplace_back("ENABLE_SSAO"); if (currentSettings.advancedRender) defines.emplace_back("ADVANCED_RENDER"); - for (auto shader : affectedShaders) { - shader->recompile(defines); + 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); + precipitation->update(delta); + particles->update(camera, delta); +} + +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)); + + auto& mainShader = assets.require("main"); + auto& entityShader = assets.require("entity"); + auto& translucentShader = assets.require("translucent"); + auto& deferredShader = assets.require("deferred_lighting").getShader(); + + const auto& settings = engine.getSettings(); + + Shader* affectedShaders[] { + &mainShader, &entityShader, &translucentShader, &deferredShader, nullptr + }; + + refreshSettings(affectedShaders); const auto& worldInfo = world->getInfo(); @@ -356,7 +350,7 @@ void WorldRenderer::renderFrame( DrawContext ctx = wctx.sub(); ctx.setDepthTest(true); ctx.setCullFace(true); - renderOpaque(ctx, camera, settings, uiDelta, pause, hudVisible); + renderOpaque(ctx, camera, settings, hudVisible); } texts->render(pctx, camera, settings, hudVisible, true); } @@ -415,7 +409,7 @@ void WorldRenderer::renderFrame( 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, pause ? 0.0f : delta, *weather); + precipitation->render(camera, *weather); } } @@ -434,7 +428,7 @@ void WorldRenderer::renderFrame( hudcam.setFov(0.9f); hudcam.position = {}; - hands->renderHands(camera, delta); + hands->render(camera); display::clearDepth(); setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f); diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index 33d8ab1bb..0412d95a3 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -1,9 +1,6 @@ #pragma once -#include -#include -#include -#include +#include "commons.hpp" #include "typedefs.hpp" @@ -11,30 +8,35 @@ #include "world/Weather.hpp" #include "window/Camera.hpp" -class Level; -class Player; -class Camera; -class Batch3D; -class LineBatch; -class ChunksRenderer; -class ParticlesRenderer; -class BlockWrapsRenderer; -class PrecipitationRenderer; -class HandsRenderer; -class NamedSkeletons; -class TextsRenderer; -class Shader; -class Frustum; -class Engine; -class LevelFrontend; -class Skybox; -class PostProcessing; -class DrawContext; -class ModelBatch; +#include +#include +#include +#include + class Assets; -class Shadows; -class GBuffer; +class Batch3D; +class BlockWrapsRenderer; +class Camera; +class ChunksRenderer; class DebugLinesRenderer; +class DrawContext; +class Engine; +class Frustum; +class GBuffer; +class HandsRenderer; +class Level; +class LevelFrontend; +class LineBatch; +class ModelBatch; +class NamedSkeletons; +class ParticlesRenderer; +class Player; +class PostProcessing; +class PrecipitationRenderer; +class Shader; +class Shadows; +class Skybox; +class TextsRenderer; struct EngineSettings; struct CompileTimeShaderSettings { @@ -93,10 +95,10 @@ class WorldRenderer { const DrawContext& context, const Camera& camera, const EngineSettings& settings, - float delta, - bool pause, bool hudVisible ); + + void refreshSettings(Shader** shaders); public: std::unique_ptr particles; std::unique_ptr texts; @@ -110,12 +112,12 @@ public: WorldRenderer(Engine& engine, LevelFrontend& frontend, Player& player); ~WorldRenderer(); + void update(const Camera& camera, float delta); + void renderFrame( const DrawContext& context, Camera& camera, bool hudVisible, - bool pause, - float delta, PostProcessing& postProcessing ); diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index e1040776d..cbb507c89 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -341,6 +341,16 @@ void Entities::update(float delta) { } updatePhysics(delta); scripting::on_entities_physics_update(delta); + + auto view = registry->view(); + for (auto [entity, transform, skeleton] : view.each()) { + if (transform.dirty) { + transform.refresh(); + } + if (skeleton.interpolation.isEnabled()) { + skeleton.interpolation.updateTimer(delta); + } + } } static void debug_render_skeleton( @@ -415,8 +425,6 @@ void Entities::render( const Assets& assets, ModelBatch& batch, const Frustum* frustum, - float delta, - bool pause, entityid_t fpsEntity ) { auto view = registry->view(); @@ -424,12 +432,6 @@ void Entities::render( if (eid.uid == fpsEntity) { continue; } - if (transform.dirty) { - transform.refresh(); - } - if (skeleton.interpolation.isEnabled()) { - skeleton.interpolation.updateTimer(delta); - } const auto& pos = transform.pos; const auto& size = transform.size; if (frustum && !frustum->isBoxVisible(pos - size, pos + size)) { diff --git a/src/objects/Entities.hpp b/src/objects/Entities.hpp index 3b3a7e1a1..804ce17f4 100644 --- a/src/objects/Entities.hpp +++ b/src/objects/Entities.hpp @@ -69,8 +69,6 @@ public: const Assets& assets, ModelBatch& batch, const Frustum* frustum, - float delta, - bool pause, entityid_t fpsEntity ); From 75afce2a680edc3bd3f706f4b71e34d27a234ad5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 2 Feb 2026 22:46:01 +0300 Subject: [PATCH 14/42] remove unused code --- src/graphics/render/ChunksRenderer.cpp | 16 ---------------- src/graphics/render/ChunksRenderer.hpp | 1 - 2 files changed, 17 deletions(-) diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index 2ba631b60..c323d6c92 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -120,22 +120,6 @@ std::shared_ptr ChunksRenderer::prepareVoxelsVolume( return voxelsBuffer; } -std::shared_ptr ChunksRenderer::prepareVoxelsVolumeDynamic( - const Chunk& chunk, int padding -) { - auto voxelsBuffer = std::make_unique( - CHUNK_W + padding * 2, CHUNK_H, CHUNK_D + padding * 2 - ); - voxelsBuffer->setPosition( - chunk.x * CHUNK_W - padding, 0, - chunk.z * CHUNK_D - padding - ); - chunks.getVoxels( - *voxelsBuffer, settings.graphics.backlight.get(), chunk.top + 3 - ); - return voxelsBuffer; -} - const Mesh* ChunksRenderer::render( const std::shared_ptr& chunk, bool important, bool lowPriority ) { diff --git a/src/graphics/render/ChunksRenderer.hpp b/src/graphics/render/ChunksRenderer.hpp index 483313379..4fc88ff69 100644 --- a/src/graphics/render/ChunksRenderer.hpp +++ b/src/graphics/render/ChunksRenderer.hpp @@ -58,7 +58,6 @@ class ChunksRenderer { size_t index, const Camera& camera, bool culling ); std::shared_ptr prepareVoxelsVolume(const Chunk& chunk); - std::shared_ptr prepareVoxelsVolumeDynamic(const Chunk& chunk, int padding); size_t enqueuedInFrame = 0; public: From ed2f0cac774c47bcd7fedb727de642673c28696b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 2 Feb 2026 23:05:44 +0300 Subject: [PATCH 15/42] add 'graphics.particles-batch-vertices' setting --- src/graphics/render/ParticlesRenderer.cpp | 6 +++--- src/graphics/render/ParticlesRenderer.hpp | 4 ++-- src/graphics/render/WorldRenderer.cpp | 2 +- src/graphics/render/WorldRenderer.hpp | 1 - src/io/settings_io.cpp | 1 + src/settings.hpp | 2 ++ 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index 8d92293d5..3c00830dd 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -19,12 +19,12 @@ ParticlesRenderer::ParticlesRenderer( const Assets& assets, const Level& level, const Chunks& chunks, - const GraphicsSettings* settings + const GraphicsSettings& settings ) : chunks(chunks), assets(assets), settings(settings), - batch(std::make_unique(4096)) { + batch(std::make_unique(settings.particlesBatchVertices.get())) { } ParticlesRenderer::~ParticlesRenderer() = default; @@ -188,7 +188,7 @@ void ParticlesRenderer::render(const Camera& camera) { aliveEmitters = emitters.size(); visibleParticles = 0; - bool backlight = settings->backlight.get(); + bool backlight = settings.backlight.get(); batch->begin(); for (auto& [texture, vec] : particles) { diff --git a/src/graphics/render/ParticlesRenderer.hpp b/src/graphics/render/ParticlesRenderer.hpp index ed9c3f314..3dcdbbb05 100644 --- a/src/graphics/render/ParticlesRenderer.hpp +++ b/src/graphics/render/ParticlesRenderer.hpp @@ -18,7 +18,7 @@ struct GraphicsSettings; class ParticlesRenderer { const Chunks& chunks; const Assets& assets; - const GraphicsSettings* settings; + const GraphicsSettings& settings; std::unordered_map> particles; std::unique_ptr batch; @@ -34,7 +34,7 @@ public: const Assets& assets, const Level& level, const Chunks& chunks, - const GraphicsSettings* settings + const GraphicsSettings& settings ); ~ParticlesRenderer(); diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index e05e7191c..e55bdd556 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -87,7 +87,7 @@ WorldRenderer::WorldRenderer( engine.getSettings() )), particles(std::make_unique( - assets, level, *player.chunks, &engine.getSettings().graphics + assets, level, *player.chunks, engine.getSettings().graphics )), texts(std::make_unique(*batch3d, assets, *frustumCulling)), blockWraps( diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index 0412d95a3..764204c2d 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -22,7 +22,6 @@ class DebugLinesRenderer; class DrawContext; class Engine; class Frustum; -class GBuffer; class HandsRenderer; class Level; class LevelFrontend; diff --git a/src/io/settings_io.cpp b/src/io/settings_io.cpp index da0027c4d..76285d733 100644 --- a/src/io/settings_io.cpp +++ b/src/io/settings_io.cpp @@ -76,6 +76,7 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) { builder.add("chunk-max-vertices", &settings.graphics.chunkMaxVertices); builder.add("chunk-max-vertices-dense", &settings.graphics.chunkMaxVerticesDense); builder.add("chunk-max-renderers", &settings.graphics.chunkMaxRenderers); + builder.add("particles-batch-vertices", &settings.graphics.particlesBatchVertices); builder.add("advanced-render", &settings.graphics.advancedRender); builder.add("ssao", &settings.graphics.ssao); builder.add("shadows-quality", &settings.graphics.shadowsQuality); diff --git a/src/settings.hpp b/src/settings.hpp index 0a0754235..c98690023 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -79,6 +79,8 @@ struct GraphicsSettings { IntegerSetting chunkMaxVerticesDense {800'000, 0, 8'000'000}; /// @brief Limit of chunk renderers count IntegerSetting chunkMaxRenderers {6, -4, 32}; + /// @brief Particles renderer vertices buffer capacity + IntegerSetting particlesBatchVertices {4'096, 0, 1'000'000}; /// @brief Advanced render pipeline FlagSetting advancedRender {true}; /// @brief Screen space ambient occlusion quality From 895a496f6ead34ea141c6b3a8ae29828fdb1892e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 3 Feb 2026 12:39:44 +0300 Subject: [PATCH 16/42] fix thing broken by refactor --- 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 c323d6c92..1b15b48e3 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -125,7 +125,7 @@ const Mesh* ChunksRenderer::render( ) { glm::ivec2 key(chunk->x, chunk->z); chunk->flags.modified = false; - if (!important) { + if (important) { ChunkMesh mesh {}; auto voxelsBuffer = prepareVoxelsVolume(*chunk); mesh = renderer->render(chunk.get(), *voxelsBuffer); From 2475a7e8f577d6a533ba5c4885eb8ac637e571cc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 3 Feb 2026 14:00:58 +0300 Subject: [PATCH 17/42] cleanup --- src/content/ContentControl.cpp | 2 - src/engine/ServerMainloop.cpp | 4 +- src/frontend/UiDocument.cpp | 6 +-- src/frontend/UiDocument.hpp | 8 +-- src/frontend/debug_panel.cpp | 12 ++--- src/frontend/hud.hpp | 2 - src/frontend/screens/LevelScreen.cpp | 5 +- src/graphics/core/Shadows.cpp | 70 +++++++++++++++----------- src/graphics/core/Shadows.hpp | 6 +-- src/graphics/render/ChunksRenderer.cpp | 8 +-- src/graphics/render/WorldRenderer.cpp | 8 +-- src/graphics/render/WorldRenderer.hpp | 4 +- src/graphics/ui/GUI.cpp | 2 +- src/logic/BlocksController.cpp | 2 +- src/logic/BlocksController.hpp | 6 +-- src/logic/EngineController.cpp | 6 +-- src/logic/LevelController.cpp | 7 ++- src/logic/LevelController.hpp | 3 +- src/logic/scripting/scripting.cpp | 2 +- src/logic/scripting/scripting.hpp | 4 +- 20 files changed, 88 insertions(+), 79 deletions(-) diff --git a/src/content/ContentControl.cpp b/src/content/ContentControl.cpp index f9b8d7736..6daf48dc0 100644 --- a/src/content/ContentControl.cpp +++ b/src/content/ContentControl.cpp @@ -52,8 +52,6 @@ std::vector& ContentControl::getBasePacks() { } void ContentControl::resetContent(const std::vector& nonReset) { - paths.setCurrentWorldFolder(""); - scripting::cleanup(nonReset); std::vector resRoots; { diff --git a/src/engine/ServerMainloop.cpp b/src/engine/ServerMainloop.cpp index 41887aeed..54698194e 100644 --- a/src/engine/ServerMainloop.cpp +++ b/src/engine/ServerMainloop.cpp @@ -1,7 +1,6 @@ #include "ServerMainloop.hpp" #include "Engine.hpp" -#include "EnginePaths.hpp" #include "logic/scripting/scripting.hpp" #include "logic/LevelController.hpp" #include "interfaces/Process.hpp" @@ -80,11 +79,10 @@ void ServerMainloop::run() { void ServerMainloop::setLevel(std::unique_ptr level) { if (level == nullptr) { controller->onWorldQuit(); - engine.getPaths().setCurrentWorldFolder(""); controller = nullptr; } else { controller = std::make_unique( - &engine, std::move(level), nullptr + engine, std::move(level), nullptr ); } } diff --git a/src/frontend/UiDocument.cpp b/src/frontend/UiDocument.cpp index c1269b879..a202c0d96 100644 --- a/src/frontend/UiDocument.cpp +++ b/src/frontend/UiDocument.cpp @@ -10,7 +10,7 @@ UiDocument::UiDocument( std::string id, - uidocscript script, + UiDocScript script, const std::shared_ptr& root, scriptenv env ) : id(std::move(id)), script(script), root(root), env(std::move(env)) { @@ -48,7 +48,7 @@ std::shared_ptr UiDocument::get(const std::string& id) const { return found->second.lock(); } -const uidocscript& UiDocument::getScript() const { +const UiDocScript& UiDocument::getScript() const { return script; } @@ -73,7 +73,7 @@ std::unique_ptr UiDocument::read( gui::UiXmlReader reader(gui, scriptenv(env)); auto view = reader.readXML(file.string(), *xmldoc->getRoot()); view->setId("root"); - uidocscript script {}; + UiDocScript script {}; auto scriptFile = io::path(file.string()+".lua"); if (io::is_regular_file(scriptFile)) { scripting::load_layout_script( diff --git a/src/frontend/UiDocument.hpp b/src/frontend/UiDocument.hpp index 8ebf7c3e6..57077bec0 100644 --- a/src/frontend/UiDocument.hpp +++ b/src/frontend/UiDocument.hpp @@ -13,7 +13,7 @@ namespace gui { class UINode; } -struct uidocscript { +struct UiDocScript { bool onopen : 1; bool onprogress : 1; bool onclose : 1; @@ -23,14 +23,14 @@ using UINodesMap = std::unordered_map>; class UiDocument { std::string id; - uidocscript script; + UiDocScript script; UINodesMap map; std::shared_ptr root; scriptenv env; public: UiDocument( std::string id, - uidocscript script, + UiDocScript script, const std::shared_ptr &root, scriptenv env ); @@ -42,7 +42,7 @@ public: const UINodesMap& getMap() const; std::shared_ptr getRoot() const; std::shared_ptr get(const std::string& id) const; - const uidocscript& getScript() const; + const UiDocScript& getScript() const; scriptenv getEnvironment() const; static std::unique_ptr read( diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index a9036b7ca..780bbf00a 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -1,9 +1,3 @@ -#include -#include -#include -#include -#include - #include "audio/audio.hpp" #include "constants.hpp" #include "content/Content.hpp" @@ -37,6 +31,12 @@ #include "world/Level.hpp" #include "world/World.hpp" +#include +#include +#include +#include +#include + using namespace gui; static std::shared_ptr