diff --git a/doc/en/block-properties.md b/doc/en/block-properties.md index fb874e0c..6400c910 100644 --- a/doc/en/block-properties.md +++ b/doc/en/block-properties.md @@ -81,6 +81,13 @@ Turns off block model shading Determines the presence of the vertex AO effect. Turned-on by default. +### *culling* + +Face culling mode: +- **default** - normal face culling +- **optional** - face culling among blocks of the same rendering group can be disabled via the `graphics.dense-render` setting. +- **disabled** - face culling among blocks of the same rendering group disabled. + ## Physics ### *obstacle* diff --git a/doc/ru/block-properties.md b/doc/ru/block-properties.md index 1862e659..366cf0fb 100644 --- a/doc/ru/block-properties.md +++ b/doc/ru/block-properties.md @@ -82,7 +82,6 @@ При значении `true` блок не препятствует прохождению вертикального луча солнечного света. - ### Без освещения - *shadeless* Выключает освещение на модели блока. @@ -91,6 +90,13 @@ Определяет наличие эффекта вершинного AO. Включен по-умолчанию. +### Отсечение - *culling* + +Режим отсечения граней: +- **default** - обычное отсечение граней +- **optional** - отсечение граней среди блоков одной группы отрисовки можно отключить через настройку `graphics.dense-render` (Плотный рендер блоков). +- **disabled** - отсечение граней среди блоков одной группы отрисовки отключено. + ## Физика ### Препятствие - *obstacle* diff --git a/res/content/base/blocks/leaves.json b/res/content/base/blocks/leaves.json index cdf9f014..e3ddae22 100644 --- a/res/content/base/blocks/leaves.json +++ b/res/content/base/blocks/leaves.json @@ -1,5 +1,7 @@ { "texture": "leaves", "material": "base:grass", + "draw-group": 5, + "culling": "optional", "base:durability": 0.7 } diff --git a/res/content/base/textures/blocks/leaves.png b/res/content/base/textures/blocks/leaves.png index 3beaf7ab..a9cd60e3 100644 Binary files a/res/content/base/textures/blocks/leaves.png and b/res/content/base/textures/blocks/leaves.png differ diff --git a/res/content/base/textures/blocks/leaves_opaque.png b/res/content/base/textures/blocks/leaves_opaque.png new file mode 100644 index 00000000..3beaf7ab Binary files /dev/null and b/res/content/base/textures/blocks/leaves_opaque.png differ diff --git a/res/layouts/pages/settings_graphics.xml.lua b/res/layouts/pages/settings_graphics.xml.lua index 8a479a1c..c4503e52 100644 --- a/res/layouts/pages/settings_graphics.xml.lua +++ b/res/layouts/pages/settings_graphics.xml.lua @@ -41,4 +41,5 @@ function on_open() create_setting("graphics.fog-curve", "Fog Curve", 0.1) create_setting("graphics.gamma", "Gamma", 0.05, "", "graphics.gamma.tooltip") create_checkbox("graphics.backlight", "Backlight", "graphics.backlight.tooltip") + create_checkbox("graphics.dense-render", "Dense blocks render", "graphics.dense-render.tooltip") end diff --git a/res/texts/en_US.txt b/res/texts/en_US.txt index c19fe00a..e9b23897 100644 --- a/res/texts/en_US.txt +++ b/res/texts/en_US.txt @@ -16,6 +16,7 @@ devtools.traceback=Traceback (most recent call first) # Tooltips graphics.gamma.tooltip=Lighting brightness curve graphics.backlight.tooltip=Backlight to prevent total darkness +graphics.dense-render.tooltip=Enables transparency in blocks like leaves # settings settings.Controls Search Mode=Search by attached button name diff --git a/res/texts/ru_RU.txt b/res/texts/ru_RU.txt index ae591042..e78b5f72 100644 --- a/res/texts/ru_RU.txt +++ b/res/texts/ru_RU.txt @@ -28,6 +28,7 @@ pack.remove-confirm=Удалить весь поставляемый паком/ # Подсказки graphics.gamma.tooltip=Кривая яркости освещения graphics.backlight.tooltip=Подсветка, предотвращающая полную темноту +graphics.dense-render.tooltip=Включает прозрачность блоков, таких как листья. # Меню menu.Apply=Применить @@ -67,6 +68,7 @@ world.delete-confirm=Удалить мир безвозвратно? # Настройки settings.Ambient=Фон settings.Backlight=Подсветка +settings.Dense blocks render=Плотный рендер блоков settings.Camera Shaking=Тряска Камеры settings.Camera Inertia=Инерция Камеры settings.Camera FOV Effects=Эффекты поля зрения diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index fee559e4..1dc676ea 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -239,10 +239,18 @@ void ContentLoader::loadBlock( } def.model = *model; } else if (!modelTypeName.empty()) { - logger.error() << "unknown model " << modelTypeName; + logger.error() << "unknown model: " << modelTypeName; def.model = BlockModel::none; } + std::string cullingModeName = to_string(def.culling); + root.at("culling").get(cullingModeName); + if (auto mode = CullingMode_from(cullingModeName)) { + def.culling = *mode; + } else { + logger.error() << "unknown culling mode: " << cullingModeName; + } + root.at("material").get(def.material); // rotation profile diff --git a/src/files/settings_io.cpp b/src/files/settings_io.cpp index d2a85f6a..fe251a11 100644 --- a/src/files/settings_io.cpp +++ b/src/files/settings_io.cpp @@ -68,6 +68,7 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) { builder.section("graphics"); builder.add("fog-curve", &settings.graphics.fogCurve); builder.add("backlight", &settings.graphics.backlight); + builder.add("dense-render", &settings.graphics.denseRender); builder.add("gamma", &settings.graphics.gamma); builder.add("frustum-culling", &settings.graphics.frustumCulling); builder.add("skybox-resolution", &settings.graphics.skyboxResolution); diff --git a/src/frontend/ContentGfxCache.cpp b/src/frontend/ContentGfxCache.cpp index c6ba4e1c..844fd4e6 100644 --- a/src/frontend/ContentGfxCache.cpp +++ b/src/frontend/ContentGfxCache.cpp @@ -6,53 +6,71 @@ #include "assets/Assets.hpp" #include "content/Content.hpp" #include "content/ContentPack.hpp" -#include "core_defs.hpp" #include "graphics/core/Atlas.hpp" #include "maths/UVRegion.hpp" #include "voxels/Block.hpp" +#include "core_defs.hpp" +#include "settings.hpp" -ContentGfxCache::ContentGfxCache(const Content* content, const Assets& assets) - : content(content) { - auto indices = content->getIndices(); + +ContentGfxCache::ContentGfxCache( + const Content& content, + const Assets& assets, + const GraphicsSettings& settings +) + : content(content), assets(assets), settings(settings) { + refresh(); +} + +void ContentGfxCache::refresh(const Block& def, const Atlas& atlas) { + for (uint side = 0; side < 6; side++) { + std::string tex = def.textureFaces[side]; + if (def.culling == CullingMode::OPTIONAL && + !settings.denseRender.get() && atlas.has(tex + "_opaque")) { + tex = tex + "_opaque"; + } + if (atlas.has(tex)) { + sideregions[def.rt.id * 6 + side] = atlas.get(tex); + } else if (atlas.has(TEXTURE_NOTFOUND)) { + sideregions[def.rt.id * 6 + side] = atlas.get(TEXTURE_NOTFOUND); + } + } + if (def.model == BlockModel::custom) { + auto model = assets.require(def.modelName); + // temporary dirty fix tbh + if (def.modelName.find(':') == std::string::npos) { + for (auto& mesh : model.meshes) { + size_t pos = mesh.texture.find(':'); + if (pos == std::string::npos) { + continue; + } + if (auto region = atlas.getIf(mesh.texture.substr(pos+1))) { + for (auto& vertex : mesh.vertices) { + vertex.uv = region->apply(vertex.uv); + } + } + } + } + models[def.rt.id] = std::move(model); + } +} + +void ContentGfxCache::refresh() { + auto indices = content.getIndices(); sideregions = std::make_unique(indices->blocks.count() * 6); const auto& atlas = assets.require("blocks"); const auto& blocks = indices->blocks.getIterable(); for (blockid_t i = 0; i < blocks.size(); i++) { auto def = blocks[i]; - for (uint side = 0; side < 6; side++) { - const std::string& tex = def->textureFaces[side]; - if (atlas.has(tex)) { - sideregions[i * 6 + side] = atlas.get(tex); - } else if (atlas.has(TEXTURE_NOTFOUND)) { - sideregions[i * 6 + side] = atlas.get(TEXTURE_NOTFOUND); - } - } - if (def->model == BlockModel::custom) { - auto model = assets.require(def->modelName); - // temporary dirty fix tbh - if (def->modelName.find(':') == std::string::npos) { - for (auto& mesh : model.meshes) { - size_t pos = mesh.texture.find(':'); - if (pos == std::string::npos) { - continue; - } - if (auto region = atlas.getIf(mesh.texture.substr(pos+1))) { - for (auto& vertex : mesh.vertices) { - vertex.uv = region->apply(vertex.uv); - } - } - } - } - models[def->rt.id] = std::move(model); - } + refresh(*def, atlas); } } ContentGfxCache::~ContentGfxCache() = default; const Content* ContentGfxCache::getContent() const { - return content; + return &content; } const model::Model& ContentGfxCache::getModel(blockid_t id) const { diff --git a/src/frontend/ContentGfxCache.hpp b/src/frontend/ContentGfxCache.hpp index 96d46ea5..cb5e2ad7 100644 --- a/src/frontend/ContentGfxCache.hpp +++ b/src/frontend/ContentGfxCache.hpp @@ -10,19 +10,29 @@ class Content; class Assets; +class Atlas; +class Block; struct UVRegion; +struct GraphicsSettings; namespace model { struct Model; } class ContentGfxCache { - const Content* content; + const Content& content; + const Assets& assets; + const GraphicsSettings& settings; + // array of block sides uv regions (6 per block) std::unique_ptr sideregions; std::unordered_map models; public: - ContentGfxCache(const Content* content, const Assets& assets); + ContentGfxCache( + const Content& content, + const Assets& assets, + const GraphicsSettings& settings + ); ~ContentGfxCache(); inline const UVRegion& getRegion(blockid_t id, int side) const { @@ -32,4 +42,8 @@ public: const model::Model& getModel(blockid_t id) const; const Content* getContent() const; + + void refresh(const Block& block, const Atlas& atlas); + + void refresh(); }; diff --git a/src/frontend/LevelFrontend.cpp b/src/frontend/LevelFrontend.cpp index 79784375..d3cbb9e8 100644 --- a/src/frontend/LevelFrontend.cpp +++ b/src/frontend/LevelFrontend.cpp @@ -14,12 +14,17 @@ #include "world/Level.hpp" LevelFrontend::LevelFrontend( - Player* currentPlayer, LevelController* controller, Assets& assets -) : level(*controller->getLevel()), - controller(controller), - assets(assets), - contentCache(std::make_unique(level.content, assets)) -{ + Player* currentPlayer, + LevelController* controller, + Assets& assets, + const EngineSettings& settings +) + : level(*controller->getLevel()), + controller(controller), + assets(assets), + contentCache(std::make_unique( + *level.content, assets, settings.graphics + )) { assets.store( BlocksPreview::build( *contentCache, assets, *level.content->getIndices() @@ -98,6 +103,10 @@ const Assets& LevelFrontend::getAssets() const { return assets; } +ContentGfxCache& LevelFrontend::getContentGfxCache() { + return *contentCache; +} + const ContentGfxCache& LevelFrontend::getContentGfxCache() const { return *contentCache; } diff --git a/src/frontend/LevelFrontend.hpp b/src/frontend/LevelFrontend.hpp index 163fc678..b53d267a 100644 --- a/src/frontend/LevelFrontend.hpp +++ b/src/frontend/LevelFrontend.hpp @@ -7,6 +7,7 @@ class Assets; class Player; class ContentGfxCache; class LevelController; +struct EngineSettings; class LevelFrontend { Level& level; @@ -14,12 +15,18 @@ class LevelFrontend { const Assets& assets; std::unique_ptr contentCache; public: - LevelFrontend(Player* currentPlayer, LevelController* controller, Assets& assets); + LevelFrontend( + Player* currentPlayer, + LevelController* controller, + Assets& assets, + const EngineSettings& settings + ); ~LevelFrontend(); Level& getLevel(); const Level& getLevel() const; const Assets& getAssets() const; const ContentGfxCache& getContentGfxCache() const; + ContentGfxCache& getContentGfxCache(); LevelController* getController() const; }; diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 34d1db67..e38b3bec 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -17,6 +17,7 @@ #include "graphics/render/Decorator.hpp" #include "graphics/ui/elements/Menu.hpp" #include "graphics/ui/GUI.hpp" +#include "frontend/ContentGfxCache.hpp" #include "logic/LevelController.hpp" #include "logic/scripting/scripting_hud.hpp" #include "util/stringutil.hpp" @@ -42,7 +43,7 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr levelPtr) controller = std::make_unique(engine, std::move(levelPtr)); frontend = std::make_unique( - controller->getPlayer(), controller.get(), assets + controller->getPlayer(), controller.get(), assets, settings ); worldRenderer = std::make_unique( engine, *frontend, controller->getPlayer() @@ -57,6 +58,11 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr levelPtr) controller->getLevel()->chunks->saveAndClear(); worldRenderer->clear(); })); + keepAlive(settings.graphics.denseRender.observe([=](bool) { + controller->getLevel()->chunks->saveAndClear(); + worldRenderer->clear(); + frontend->getContentGfxCache().refresh(); + })); keepAlive(settings.camera.fov.observe([=](double value) { controller->getPlayer()->fpCamera->setFov(glm::radians(value)); })); diff --git a/src/graphics/render/BlocksRenderer.cpp b/src/graphics/render/BlocksRenderer.cpp index cb590bcb..56f1c4e2 100644 --- a/src/graphics/render/BlocksRenderer.cpp +++ b/src/graphics/render/BlocksRenderer.cpp @@ -8,9 +8,6 @@ #include "voxels/Chunks.hpp" #include "lighting/Lightmap.hpp" #include "frontend/ContentGfxCache.hpp" -#include "settings.hpp" - -#include const glm::vec3 BlocksRenderer::SUN_VECTOR (0.411934f, 0.863868f, -0.279161f); @@ -342,41 +339,41 @@ void BlocksRenderer::blockCube( } if (ao) { - if (isOpen(coord + Z, group)) { + if (isOpen(coord + Z, block)) { faceAO(coord, X, Y, Z, texfaces[5], lights); } - if (isOpen(coord - Z, group)) { + if (isOpen(coord - Z, block)) { faceAO(coord, -X, Y, -Z, texfaces[4], lights); } - if (isOpen(coord + Y, group)) { + if (isOpen(coord + Y, block)) { faceAO(coord, X, -Z, Y, texfaces[3], lights); } - if (isOpen(coord - Y, group)) { + if (isOpen(coord - Y, block)) { faceAO(coord, X, Z, -Y, texfaces[2], lights); } - if (isOpen(coord + X, group)) { + if (isOpen(coord + X, block)) { faceAO(coord, -Z, Y, X, texfaces[1], lights); } - if (isOpen(coord - X, group)) { + if (isOpen(coord - X, block)) { faceAO(coord, Z, Y, -X, texfaces[0], lights); } } else { - if (isOpen(coord + Z, group)) { + if (isOpen(coord + Z, block)) { face(coord, X, Y, Z, texfaces[5], pickLight(coord + Z), lights); } - if (isOpen(coord - Z, group)) { + if (isOpen(coord - Z, block)) { face(coord, -X, Y, -Z, texfaces[4], pickLight(coord - Z), lights); } - if (isOpen(coord + Y, group)) { + if (isOpen(coord + Y, block)) { face(coord, X, -Z, Y, texfaces[3], pickLight(coord + Y), lights); } - if (isOpen(coord - Y, group)) { + if (isOpen(coord - Y, block)) { face(coord, X, Z, -Y, texfaces[2], pickLight(coord - Y), lights); } - if (isOpen(coord + X, group)) { + if (isOpen(coord + X, block)) { face(coord, -Z, Y, X, texfaces[1], pickLight(coord + X), lights); } - if (isOpen(coord - X, group)) { + if (isOpen(coord - X, block)) { face(coord, Z, Y, -X, texfaces[0], pickLight(coord - X), lights); } } diff --git a/src/graphics/render/BlocksRenderer.hpp b/src/graphics/render/BlocksRenderer.hpp index c0e0086e..c652b6a0 100644 --- a/src/graphics/render/BlocksRenderer.hpp +++ b/src/graphics/render/BlocksRenderer.hpp @@ -13,6 +13,7 @@ #include "graphics/core/MeshData.hpp" #include "maths/util.hpp" #include "commons.hpp" +#include "settings.hpp" class Content; class Mesh; @@ -22,7 +23,6 @@ class Chunks; class VoxelsVolume; class Chunks; class ContentGfxCache; -struct EngineSettings; struct UVRegion; class BlocksRenderer { @@ -118,7 +118,7 @@ class BlocksRenderer { 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, ubyte group) const { + inline bool isOpen(const glm::ivec3& pos, const Block& def) const { auto id = voxelsBuffer->pickBlockId( chunk->x * CHUNK_W + pos.x, pos.y, chunk->z * CHUNK_D + pos.z ); @@ -126,7 +126,13 @@ class BlocksRenderer { return false; } const auto& block = *blockDefsCache[id]; - if ((block.drawGroup != group && block.lightPassing) || !block.rt.solid) { + if (((block.drawGroup != def.drawGroup) && block.drawGroup) || !block.rt.solid) { + return true; + } + if ((def.culling == CullingMode::DISABLED || + (def.culling == CullingMode::OPTIONAL && + settings.graphics.denseRender.get())) && + id == def.rt.id) { return true; } return !id; diff --git a/src/graphics/render/Decorator.hpp b/src/graphics/render/Decorator.hpp index fbc196aa..422f6f77 100644 --- a/src/graphics/render/Decorator.hpp +++ b/src/graphics/render/Decorator.hpp @@ -14,7 +14,7 @@ class Chunks; class Camera; class Assets; class Player; -struct Block; +class Block; class Engine; class LevelController; class WorldRenderer; diff --git a/src/graphics/render/ModelsGenerator.hpp b/src/graphics/render/ModelsGenerator.hpp index 52bc56d0..c4665118 100644 --- a/src/graphics/render/ModelsGenerator.hpp +++ b/src/graphics/render/ModelsGenerator.hpp @@ -7,7 +7,7 @@ struct ItemDef; class Assets; class Content; -struct Block; +class Block; class ModelsGenerator { public: diff --git a/src/settings.hpp b/src/settings.hpp index b0eee035..48d72b27 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -63,16 +63,22 @@ struct GraphicsSettings { NumberSetting gamma {1.0f, 0.4f, 1.0f}; /// @brief Enable blocks backlight to prevent complete darkness FlagSetting backlight {true}; + /// @brief Disable culling with 'optional' mode + FlagSetting denseRender {true}; /// @brief Enable chunks frustum culling FlagSetting frustumCulling {true}; + /// @brief Skybox texture face resolution IntegerSetting skyboxResolution {64 + 32, 64, 128}; + /// @brief Chunk renderer vertices buffer capacity IntegerSetting chunkMaxVertices {200'000, 0, 4'000'000}; + /// @brief Limit of chunk renderers count IntegerSetting chunkMaxRenderers {6, -4, 32}; }; struct DebugSettings { /// @brief Turns off chunks saving/loading FlagSetting generatorTestMode {false}; + /// @brief Write lights cache FlagSetting doWriteLights {true}; }; diff --git a/src/voxels/Block.cpp b/src/voxels/Block.cpp index 520f9f99..96bca245 100644 --- a/src/voxels/Block.cpp +++ b/src/voxels/Block.cpp @@ -49,6 +49,30 @@ std::optional BlockModel_from(std::string_view str) { return std::nullopt; } +std::string to_string(CullingMode mode) { + switch (mode) { + case CullingMode::DEFAULT: + return "default"; + case CullingMode::OPTIONAL: + return "optional"; + case CullingMode::DISABLED: + return "disabled"; + default: + return "unknown"; + } +} + +std::optional CullingMode_from(std::string_view str) { + if (str == "default") { + return CullingMode::DEFAULT; + } else if (str == "optional") { + return CullingMode::OPTIONAL; + } else if (str == "disabled") { + return CullingMode::DISABLED; + } + return std::nullopt; +} + CoordSystem::CoordSystem(glm::ivec3 axisX, glm::ivec3 axisY, glm::ivec3 axisZ) : axisX(axisX), axisY(axisY), axisZ(axisZ) { fix = glm::ivec3(0); diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index 557bf04c..eb232e8d 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -97,6 +97,15 @@ enum class BlockModel { std::string to_string(BlockModel model); std::optional BlockModel_from(std::string_view str); +enum class CullingMode { + DEFAULT, + OPTIONAL, + DISABLED, +}; + +std::string to_string(CullingMode mode); +std::optional CullingMode_from(std::string_view str); + using BoxModel = AABB; /// @brief Common kit of block properties applied to groups of blocks @@ -142,6 +151,9 @@ public: std::string modelName = ""; + /// @brief Culling mode + CullingMode culling = CullingMode::DEFAULT; + /// @brief Does the block passing lights into itself bool lightPassing = false;