From e24a5db97ab57806a2ad95133f3fbdfdba1e6197 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Feb 2026 01:09:30 +0300 Subject: [PATCH 01/12] test clouds --- src/graphics/core/Shadows.cpp | 54 +++--- src/graphics/render/CloudsRenderer.cpp | 251 +++++++++++++++++++++++++ src/graphics/render/CloudsRenderer.hpp | 19 ++ src/graphics/render/WorldRenderer.cpp | 79 ++++---- src/graphics/render/WorldRenderer.hpp | 2 + src/voxels/VoxelsVolume.cpp | 55 ++++++ src/voxels/VoxelsVolume.hpp | 18 +- 7 files changed, 409 insertions(+), 69 deletions(-) create mode 100755 src/graphics/render/CloudsRenderer.cpp create mode 100755 src/graphics/render/CloudsRenderer.hpp create mode 100644 src/voxels/VoxelsVolume.cpp diff --git a/src/graphics/core/Shadows.cpp b/src/graphics/core/Shadows.cpp index 19ba7d000..c9ecc108d 100644 --- a/src/graphics/core/Shadows.cpp +++ b/src/graphics/core/Shadows.cpp @@ -130,21 +130,22 @@ void Shadows::refresh( const std::function& renderShadowPass ) { static int frameid = 0; - if (shadows) { - if (frameid % 2 == 0) { - generateShadowsMap( - camera, pctx, *shadowMap, shadowCamera, 1.0f, renderShadowPass - ); - } else { - generateShadowsMap( - camera, - pctx, - *wideShadowMap, - wideShadowCamera, - 3.0f, - renderShadowPass - ); - } + if (!shadows) { + return; + } + if (frameid % 2 == 0) { + generateShadowsMap( + camera, pctx, *shadowMap, shadowCamera, 1.0f, renderShadowPass + ); + } else { + generateShadowsMap( + camera, + pctx, + *wideShadowMap, + wideShadowCamera, + 3.0f, + renderShadowPass + ); } frameid++; } @@ -176,10 +177,7 @@ void Shadows::generateShadowsMap( shadowCamera.setAspectRatio(1.0f); float t = worldInfo.daytime - 0.25f; - if (t < 0.0f) { - t += 1.0f; - } - t = fmod(t, 0.5f); + t = glm::mod(t < 0.0f ? t + 1.0f : t, 0.5f); float sunCycleStep = 1.0f / 500.0f; float sunAngle = glm::radians( @@ -206,15 +204,13 @@ void Shadows::generateShadowsMap( shadowCamera.setProjection(glm::ortho(min.x, max.x, min.y, max.y, 0.1f, 1000.0f)); - { - auto sctx = pctx.sub(); - sctx.setDepthTest(true); - sctx.setCullFace(true); - sctx.setViewport({resolution, resolution}); - shadowMap.bind(); - if (renderShadowPass) { - renderShadowPass(shadowCamera); - } - shadowMap.unbind(); + auto sctx = pctx.sub(); + sctx.setDepthTest(true); + sctx.setCullFace(true); + sctx.setViewport({resolution, resolution}); + shadowMap.bind(); + if (renderShadowPass) { + renderShadowPass(shadowCamera); } + shadowMap.unbind(); } diff --git a/src/graphics/render/CloudsRenderer.cpp b/src/graphics/render/CloudsRenderer.cpp new file mode 100755 index 000000000..c013f958f --- /dev/null +++ b/src/graphics/render/CloudsRenderer.cpp @@ -0,0 +1,251 @@ +#include "CloudsRenderer.hpp" + +#include "assets/Assets.hpp" +#include "coders/imageio.hpp" +#include "debug/Logger.hpp" +#include "graphics/core/ImageData.hpp" +#include "graphics/core/Mesh.hpp" +#include "graphics/core/Shader.hpp" +#include "graphics/core/Texture.hpp" +#include "io/io.hpp" +#include "lighting/Lightmap.hpp" +#include "maths/FastNoiseLite.h" +#include "util/timeutil.hpp" +#include "window/Camera.hpp" + +#include +#include + +static debug::Logger logger("clouds-render"); + +class CloudsMap { +public: + glm::ivec3 size; + const bool* voxels; + + CloudsMap() = default; + + CloudsMap(const glm::ivec3& size, const bool* voxels) + : size(size), voxels(voxels) { + } + + bool isOpen(int x, int y, int z) const { + if (x >= 0 && x < size.x && y >= 0 && y < size.y && z >= 0 && z < size.z) { + return !voxels[vox_index(x, y, z, size.x, size.z)]; + } + return true; + } +}; + + +class VolumeRenderer final { +public: + VolumeRenderer(size_t capacity) + : vertices(std::make_unique(capacity)), + indexBuffer(std::make_unique(capacity * 6)), + capacity(capacity) { + } + + ~VolumeRenderer() = default; + + void build(CloudsMap volumeMap) { + this->map = std::move(volumeMap); + overflow = false; + offset = 0; + indexOffset = 0; + indexCount = 0; + + int end = map.size.x * map.size.y * map.size.z; + for (int i = 0; i < end; i++) { + int x = i % map.size.x; + int y = i / (map.size.z * map.size.x); + int z = (i / map.size.x) % map.size.z; + const auto& vox = map.voxels[i]; + if (!vox) { + continue; + } + cube({x, y, z}); + if (overflow) { + return; + } + } + logger.info() << "layer vertices: " << offset; + } + + MeshData createMesh() const { + return MeshData( + util::Buffer(vertices.get(), offset), + std::vector> { + util::Buffer(indexBuffer.get(), indexCount), + }, + util::Buffer( + ChunkVertex::ATTRIBUTES, + sizeof(ChunkVertex::ATTRIBUTES) / sizeof(VertexAttribute) + ) + ); + } + +private: + std::unique_ptr vertices; + std::unique_ptr indexBuffer; + size_t capacity; + uint32_t indexOffset = 0; + uint32_t indexCount = 0; + uint32_t offset = 0; + bool overflow = false; + CloudsMap map {}; + + void vertex( + const glm::vec3& coord, + const glm::vec3& normal + ) { + auto& vert = vertices[offset++]; + vert.position = coord; + vert.uv = {}; + vert.normal = { + static_cast(normal.r * 127 + 128), + static_cast(normal.g * 127 + 128), + static_cast(normal.b * 127 + 128), + 255 + }; + vert.color = { + 0, 0, 0, static_cast((coord.y / 8.0f * 0.25f + 0.75f) * 255) + }; + } + + void face( + const glm::vec3& coord, + const glm::vec3& X, + const glm::vec3& Y, + const glm::vec3& Z + ) { + if (!isOpen(coord + Z)) { + return; + } + if (offset + 4 >= capacity) { + overflow = true; + return; + } + + float s = 0.5f; + vertex(coord + (-X - Y + Z) * s, Z); + vertex(coord + ( X - Y + Z) * s, Z); + vertex(coord + ( X + Y + Z) * s, Z); + vertex(coord + (-X + Y + Z) * s, Z); + + const uint32_t indices[] {0, 1, 2, 0, 2, 3}; + for (size_t i = 0; i < 6; i++) { + indexBuffer[indexCount++] = indexOffset + indices[i]; + } + indexOffset += 4; + } + + bool isOpen(const glm::ivec3& pos) const { + return pos.y < 0 || pos.y >= map.size.y || + map.isOpen(pos.x, pos.y, pos.z); + } + + void cube(const glm::ivec3& coord) { + const glm::ivec3 X(1, 0, 0); + const glm::ivec3 Y(0, 1, 0); + const glm::ivec3 Z(0, 0, 1); + + face(coord, X, Y, Z); + face(coord, -X, Y, -Z); + face(coord, X, -Z, Y); + face(coord, X, Z, -Y); + face(coord, -Z, Y, X); + face(coord, Z, Y, -X); + } +}; + +static inline constexpr int WIDTH = 256 / 2; +static inline constexpr int DEPTH = 256 / 2; + + +CloudsRenderer::CloudsRenderer(const Assets& assets) : assets(assets) { + timeutil::ScopeLogTimer log(111); + + VolumeRenderer volumeRenderer(1024 * 1024); + + for (int layer = 0; layer < 2; layer++) { + fnl_state state = fnlCreateState(); + const int w = WIDTH; + const int h = 8; + const int d = DEPTH; + + state.seed = 52365 + layer * 3521; + + bool voxels[w * h * d]; + for (int y = 0; y < h; y++) { + for (int z = 0; z < d; z++) { + for (int x = 0; x < w; x++) { + float s = 2.0f; + auto n = fnlGetNoise2D(&state, x * s, z * s); + n += fnlGetNoise2D(&state, x * s + fnlGetNoise2D(&state, x * s * 4 + 2, z * s * 4) * 0.25f, z * 3.0f) * 0.5f; + n += fnlGetNoise2D(&state, x * s * 2, z * s * 2) * 0.25f; + n += fnlGetNoise2D(&state, x * s * 4, z * s * 4) * 0.125f * 2; + n += fnlGetNoise2D(&state, x * s * 8, z * s * 8) * 0.125f * 0.5f * 2; + n += fnlGetNoise2D(&state, x * s * 16, z * s * 16) * 0.125f * 0.25f * 3; + n = glm::max(0.0f, n); + // n = n * n * 2.0f; + n -= 0.2f + layer * 0.5f; + // n -= 0.5f; + + bool solid = y <= n * h && y >= (0.5f - n * 0.5f) * h; + voxels[vox_index(x, y, z, w, d)] = solid; + } + } + } + + CloudsMap map({w, h, d}, voxels); + + timeutil::ScopeLogTimer log2(222); + volumeRenderer.build(map); + testMeshes[layer] = std::make_unique>(volumeRenderer.createMesh()); + } +} + +CloudsRenderer::~CloudsRenderer() = default; + +#include "util/timeutil.hpp" + +void CloudsRenderer::draw(float timer, float fogFactor, const Camera& camera) { + auto& shader = assets.require("main"); + assets.require("misc/blank").bind(); + + float scale = 15; + float totalWidth = WIDTH * scale; + float totalDepth = DEPTH * scale; + + int cellX = glm::round(camera.position.x / totalWidth); + int cellZ = glm::round(camera.position.z / totalDepth); + + float speed = 10.0f; + + timeutil::ScopeLogTimer log(555); + + for (int i = 0; i < 2; i++) { + float speedX = glm::sin(i * 0.3f) * speed / (i + 1); + float speedZ = -glm::cos(i * 0.3f) * speed / (i + 1); + for (int x = -2; x <= 2; x++) { + for (int z = -2; z <= 2; z++) { + auto matrix = glm::mat4(1.0f); + // matrix = glm::rotate(matrix, timer * 0.003f * (i % 2 * 2 - 1) + i, glm::vec3(0, 1, 0)); + matrix = glm::translate( + matrix, + glm::vec3( + -128 * scale + (x + cellX) * WIDTH * scale + glm::mod(timer * speedX, totalWidth), + 250 + i * 200, + -128 * scale + (z + cellZ) * DEPTH * scale + glm::mod(timer * speedZ, totalDepth) + ) + ); + matrix = glm::scale(matrix, glm::vec3(scale, scale, scale)); + shader.uniform1f("u_fogFactor", fogFactor * 0.03f); + shader.uniform1f("u_fogCurve", 0.7f - 0.3f); + shader.uniformMatrix("u_model", matrix); + testMeshes[i]->draw(); + } + } + } +} diff --git a/src/graphics/render/CloudsRenderer.hpp b/src/graphics/render/CloudsRenderer.hpp new file mode 100755 index 000000000..2eec12e53 --- /dev/null +++ b/src/graphics/render/CloudsRenderer.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "commons.hpp" + +#include + +class Assets; +class Camera; + +class CloudsRenderer final { +public: + CloudsRenderer(const Assets& assets); + ~CloudsRenderer(); + + void draw(float timer, float fogFactor, const Camera& camera); +private: + const Assets& assets; + std::array>, 2> testMeshes; +}; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 98a065f3c..b00a6756e 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -50,6 +50,7 @@ #include "Skybox.hpp" #include "Emitter.hpp" #include "TextNote.hpp" +#include "CloudsRenderer.hpp" #include #include @@ -116,6 +117,7 @@ WorldRenderer::WorldRenderer( ); shadowMapping = std::make_unique(level); debugLines = std::make_unique(level); + cloudsRenderer = std::make_unique(assets); } WorldRenderer::~WorldRenderer() = default; @@ -210,6 +212,7 @@ void WorldRenderer::renderOpaque( setupWorldShader(shader, camera, settings, fogFactor); chunksRenderer->drawChunks(camera, shader); + cloudsRenderer->draw(timer, fogFactor, camera); blockWraps->draw(ctx, player); if (hudVisible) { @@ -369,7 +372,7 @@ void WorldRenderer::renderFrame( if (gbufferPipeline) { postProcessing.bindDepthBuffer(); } else { - postProcessing.getFramebuffer()->bind(); + ctx.setFramebuffer(postProcessing.getFramebuffer()); } // Background sky plane @@ -448,43 +451,45 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) { int y = std::floor(player.currentCamera->position.y); int z = std::floor(player.currentCamera->position.z); auto block = player.chunks->get(x, y, z); - if (block && block->id) { - const auto& def = level.content.getIndices()->blocks.require(block->id); - if (def.overlayTexture.empty()) { - return; - } - auto textureRegion = util::get_texture_region( - assets, def.overlayTexture, "blocks:notfound" - ); - DrawContext ctx = wctx.sub(); - ctx.setDepthTest(false); - ctx.setCullFace(false); - - auto& shader = assets.require("ui3d"); - shader.use(); - batch3d->begin(); - shader.uniformMatrix("u_projview", glm::mat4(1.0f)); - shader.uniformMatrix("u_apply", glm::mat4(1.0f)); - auto light = player.chunks->getLight(x, y, z); - float s = Lightmap::extract(light, 3) / 15.0f; - glm::vec4 tint( - glm::min(1.0f, Lightmap::extract(light, 0) / 15.0f + s), - glm::min(1.0f, Lightmap::extract(light, 1) / 15.0f + s), - glm::min(1.0f, Lightmap::extract(light, 2) / 15.0f + s), - 1.0f - ); - batch3d->texture(textureRegion.texture); - batch3d->sprite( - glm::vec3(), - glm::vec3(0, 1, 0), - glm::vec3(1, 0, 0), - 2, - 2, - textureRegion.region, - tint - ); - batch3d->flush(); + + if (block == nullptr || block->id == BLOCK_AIR || block->id == BLOCK_VOID) { + return; } + const auto& def = level.content.getIndices()->blocks.require(block->id); + if (def.overlayTexture.empty()) { + return; + } + auto textureRegion = util::get_texture_region( + assets, def.overlayTexture, "blocks:notfound" + ); + DrawContext ctx = wctx.sub(); + ctx.setDepthTest(false); + ctx.setCullFace(false); + + auto& shader = assets.require("ui3d"); + shader.use(); + batch3d->begin(); + shader.uniformMatrix("u_projview", glm::mat4(1.0f)); + shader.uniformMatrix("u_apply", glm::mat4(1.0f)); + auto light = player.chunks->getLight(x, y, z); + float s = Lightmap::extract(light, 3) / 15.0f; + glm::vec4 tint( + glm::min(1.0f, Lightmap::extract(light, 0) / 15.0f + s), + glm::min(1.0f, Lightmap::extract(light, 1) / 15.0f + s), + glm::min(1.0f, Lightmap::extract(light, 2) / 15.0f + s), + 1.0f + ); + batch3d->texture(textureRegion.texture); + batch3d->sprite( + glm::vec3(), + glm::vec3(0, 1, 0), + glm::vec3(1, 0, 0), + 2, + 2, + textureRegion.region, + tint + ); + batch3d->flush(); } void WorldRenderer::clear() { diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index d3b1b1a95..b9a893d16 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -34,6 +34,7 @@ class Shader; class Shadows; class Skybox; class TextsRenderer; +class CloudsRenderer; struct EngineSettings; struct CompileTimeShaderSettings { @@ -57,6 +58,7 @@ class WorldRenderer { std::unique_ptr shadowMapping; std::unique_ptr debugLines; std::unique_ptr precipitation; + std::unique_ptr cloudsRenderer; Weather weather {}; float timer = 0.0f; diff --git a/src/voxels/VoxelsVolume.cpp b/src/voxels/VoxelsVolume.cpp new file mode 100644 index 000000000..86ba1a32f --- /dev/null +++ b/src/voxels/VoxelsVolume.cpp @@ -0,0 +1,55 @@ +#include "VoxelsVolume.hpp" + +#include "constants.hpp" +#include "content/Content.hpp" +#include "voxels/Block.hpp" + +#include + +void VoxelsVolume::compressInto(VoxelsVolume& dst, const Content& content) const { + assert(dst.w < w && dst.h < h && dst.d < d); + assert(w % dst.w == 0 && h % dst.h == 0 && d % dst.d == 0); + + int stepW = w / dst.w; + int stepH = h / dst.h; + int stepD = d / dst.d; + int height = dst.h; + int depth = dst.d; + int width = dst.w; + + auto dstVoxels = dst.getVoxels(); + auto dstLights = dst.getLights(); + + const auto& blockDefs = content.getIndices()->blocks; + + for (int y = 0; y < height; y++) { + for (int z = 0; z < depth; z++) { + for (int x = 0; x < width; x++) { + voxel selectedVoxel {BLOCK_AIR, {}}; + light_t light = 0; + for (int ly = 0; ly < stepH; ly++) { + for (int lz = 0; lz < stepD; lz++) { + for (int lx = 0; lx < stepW; lx++) { + size_t srcIndex = vox_index( + x * stepW + lx, y * stepH + ly, z * stepD, w, d + ); + auto vox = voxels[srcIndex]; + if (vox.id == BLOCK_VOID) { + continue; + } + const auto& def = blockDefs.require(vox.id); + if (def.rt.solid) { + selectedVoxel = std::move(vox); + } else if (light == 0) { + light = lights[srcIndex]; + } + } + } + } + size_t dstIndex = vox_index(x, y, z, dst.w, dst.d); + dstLights[dstIndex] = light; + dstVoxels[dstIndex] = std::move(selectedVoxel); + } + } + } +} diff --git a/src/voxels/VoxelsVolume.hpp b/src/voxels/VoxelsVolume.hpp index 14ac0a4f4..471b159c5 100644 --- a/src/voxels/VoxelsVolume.hpp +++ b/src/voxels/VoxelsVolume.hpp @@ -6,15 +6,17 @@ #include +class Content; + 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) { + : 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)) { std::memset(voxels.get(), 0xFF, sizeof(voxel) * w * h * d); } @@ -52,10 +54,18 @@ public: return voxels.get(); } + const voxel* getVoxels() const { + return voxels.get(); + } + light_t* getLights() { return lights.get(); } + const light_t* getLights() const { + return lights.get(); + } + 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) { @@ -80,6 +90,8 @@ public: } return lights[vox_index(bx - x, by - y, bz - z, w, d)]; } + + void compressInto(VoxelsVolume& dst, const Content& content) const; private: int x, y, z; int w, h, d; From ef16b01159daafb03c37353c254796c769c16212 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Feb 2026 01:10:42 +0300 Subject: [PATCH 02/12] update workflows --- .github/workflows/appimage.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows-clang.yml | 2 +- .github/workflows/windows.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 92c9d9fd6..c67ef2fe0 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main", "dev" ] + branches: [ "main", "volume-renderer" ] workflow_call: inputs: build_name: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 99466ddab..675966354 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main", "dev" ] + branches: [ "main", "volume-renderer" ] workflow_call: inputs: build_name: diff --git a/.github/workflows/windows-clang.yml b/.github/workflows/windows-clang.yml index af0b060ae..384364368 100644 --- a/.github/workflows/windows-clang.yml +++ b/.github/workflows/windows-clang.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main", "dev" ] + branches: [ "main", "volume-renderer" ] workflow_call: inputs: build_name: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index d13479cd3..5f95321c7 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main", "dev" ] + branches: [ "main", "volume-renderer" ] jobs: build-windows: From a1cb939b9e2b032d374d8db417ff41fdc132ad78 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Feb 2026 13:56:56 +0300 Subject: [PATCH 03/12] test looped clouds --- src/graphics/render/CloudsRenderer.cpp | 66 ++++++++++++++++---------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/src/graphics/render/CloudsRenderer.cpp b/src/graphics/render/CloudsRenderer.cpp index c013f958f..09a010d4d 100755 --- a/src/graphics/render/CloudsRenderer.cpp +++ b/src/graphics/render/CloudsRenderer.cpp @@ -173,25 +173,43 @@ CloudsRenderer::CloudsRenderer(const Assets& assets) : assets(assets) { const int w = WIDTH; const int h = 8; const int d = DEPTH; + const int dd = d * 1.5; - state.seed = 52365 + layer * 3521; + state.seed = 5265 + layer * 3521; + + float pi2 = glm::two_pi(); + + float heightmap[w * dd]; + for (int lz = 0; lz < dd; lz++) { + for (int lx = 0; lx < w; lx++) { + float x = glm::sin(lx / static_cast(w) * pi2) * w / pi2; + float y = -glm::cos(lx / static_cast(w) * pi2) * w / pi2; + float z = lz; + float s = 2.0f; + auto n = fnlGetNoise3D(&state, x * s, y * s, z * s); + n += fnlGetNoise3D(&state, x * s + fnlGetNoise2D(&state, x * s * 4 + 2, z * s * 4) * 0.25f, y * s, z * 3.0f) * 0.5f; + n += fnlGetNoise3D(&state, x * s * 2, y * s * 2, z * s * 2) * 0.25f; + n += fnlGetNoise3D(&state, x * s * 4, y * s * 4, z * s * 4) * 0.125f * 2; + n += fnlGetNoise3D(&state, x * s * 8, y * s * 8, z * s * 8) * 0.125f * 0.5f * 2; + n += fnlGetNoise3D(&state, x * s * 16, y * s * 16, z * s * 16) * 0.125f * 0.25f * 3; + n = glm::max(0.0f, n); + // n = n * n * 2.0f; + n -= 0.2f + layer * 0.5f; + // n -= 0.5f; + + heightmap[lz * w + lx] = n; + } + } bool voxels[w * h * d]; for (int y = 0; y < h; y++) { for (int z = 0; z < d; z++) { for (int x = 0; x < w; x++) { - float s = 2.0f; - auto n = fnlGetNoise2D(&state, x * s, z * s); - n += fnlGetNoise2D(&state, x * s + fnlGetNoise2D(&state, x * s * 4 + 2, z * s * 4) * 0.25f, z * 3.0f) * 0.5f; - n += fnlGetNoise2D(&state, x * s * 2, z * s * 2) * 0.25f; - n += fnlGetNoise2D(&state, x * s * 4, z * s * 4) * 0.125f * 2; - n += fnlGetNoise2D(&state, x * s * 8, z * s * 8) * 0.125f * 0.5f * 2; - n += fnlGetNoise2D(&state, x * s * 16, z * s * 16) * 0.125f * 0.25f * 3; - n = glm::max(0.0f, n); - // n = n * n * 2.0f; - n -= 0.2f + layer * 0.5f; - // n -= 0.5f; - + float n = heightmap[z * w + x]; + if (z < d / 2) { + float t = z / static_cast(d / 2); + n = n * t + heightmap[(d + z) * w + x] * (1.0f - t); + } bool solid = y <= n * h && y >= (0.5f - n * 0.5f) * h; voxels[vox_index(x, y, z, w, d)] = solid; } @@ -208,8 +226,6 @@ CloudsRenderer::CloudsRenderer(const Assets& assets) : assets(assets) { CloudsRenderer::~CloudsRenderer() = default; -#include "util/timeutil.hpp" - void CloudsRenderer::draw(float timer, float fogFactor, const Camera& camera) { auto& shader = assets.require("main"); assets.require("misc/blank").bind(); @@ -218,26 +234,24 @@ void CloudsRenderer::draw(float timer, float fogFactor, const Camera& camera) { float totalWidth = WIDTH * scale; float totalDepth = DEPTH * scale; - int cellX = glm::round(camera.position.x / totalWidth); - int cellZ = glm::round(camera.position.z / totalDepth); - - float speed = 10.0f; - - timeutil::ScopeLogTimer log(555); + int cellX = glm::floor(camera.position.x / totalWidth + 0.5f); + int cellZ = glm::floor(camera.position.z / totalDepth + 0.5f); + float speed = 10.0f * 100.0; for (int i = 0; i < 2; i++) { - float speedX = glm::sin(i * 0.3f) * speed / (i + 1); - float speedZ = -glm::cos(i * 0.3f) * speed / (i + 1); + float speedX = glm::sin(i * 0.3f + 0.4f) * speed / (i + 1); + float speedZ = -glm::cos(i * 0.3f + 0.4f) * speed / (i + 1); for (int x = -2; x <= 2; x++) { for (int z = -2; z <= 2; z++) { auto matrix = glm::mat4(1.0f); - // matrix = glm::rotate(matrix, timer * 0.003f * (i % 2 * 2 - 1) + i, glm::vec3(0, 1, 0)); matrix = glm::translate( matrix, glm::vec3( - -128 * scale + (x + cellX) * WIDTH * scale + glm::mod(timer * speedX, totalWidth), + -128 * scale + (x + cellX) * WIDTH * scale + + glm::mod(timer * speedX, totalWidth), 250 + i * 200, - -128 * scale + (z + cellZ) * DEPTH * scale + glm::mod(timer * speedZ, totalDepth) + -128 * scale + (z + cellZ) * DEPTH * scale + + glm::mod(timer * speedZ, totalDepth) ) ); matrix = glm::scale(matrix, glm::vec3(scale, scale, scale)); From 28cd59ebdebcf3899357affb42a3cdfe36f59a79 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Feb 2026 14:06:40 +0300 Subject: [PATCH 04/12] calibration --- src/graphics/render/CloudsRenderer.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/graphics/render/CloudsRenderer.cpp b/src/graphics/render/CloudsRenderer.cpp index 09a010d4d..6b38ee215 100755 --- a/src/graphics/render/CloudsRenderer.cpp +++ b/src/graphics/render/CloudsRenderer.cpp @@ -159,13 +159,11 @@ private: } }; -static inline constexpr int WIDTH = 256 / 2; -static inline constexpr int DEPTH = 256 / 2; +static inline constexpr int WIDTH = 256; +static inline constexpr int DEPTH = 256; CloudsRenderer::CloudsRenderer(const Assets& assets) : assets(assets) { - timeutil::ScopeLogTimer log(111); - VolumeRenderer volumeRenderer(1024 * 1024); for (int layer = 0; layer < 2; layer++) { @@ -185,7 +183,7 @@ CloudsRenderer::CloudsRenderer(const Assets& assets) : assets(assets) { float x = glm::sin(lx / static_cast(w) * pi2) * w / pi2; float y = -glm::cos(lx / static_cast(w) * pi2) * w / pi2; float z = lz; - float s = 2.0f; + float s = 1.5f; auto n = fnlGetNoise3D(&state, x * s, y * s, z * s); n += fnlGetNoise3D(&state, x * s + fnlGetNoise2D(&state, x * s * 4 + 2, z * s * 4) * 0.25f, y * s, z * 3.0f) * 0.5f; n += fnlGetNoise3D(&state, x * s * 2, y * s * 2, z * s * 2) * 0.25f; @@ -230,14 +228,14 @@ void CloudsRenderer::draw(float timer, float fogFactor, const Camera& camera) { auto& shader = assets.require("main"); assets.require("misc/blank").bind(); - float scale = 15; + float scale = 8; float totalWidth = WIDTH * scale; float totalDepth = DEPTH * scale; int cellX = glm::floor(camera.position.x / totalWidth + 0.5f); int cellZ = glm::floor(camera.position.z / totalDepth + 0.5f); - float speed = 10.0f * 100.0; + float speed = 10.0f; for (int i = 0; i < 2; i++) { float speedX = glm::sin(i * 0.3f + 0.4f) * speed / (i + 1); float speedZ = -glm::cos(i * 0.3f + 0.4f) * speed / (i + 1); From 8153ddde89ae038dfde978b05f91c75fe2799e55 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 12 Feb 2026 18:19:25 +0300 Subject: [PATCH 05/12] add clouds shader --- res/preload.json | 3 +- res/presets/weather/cloudy.json | 2 +- res/presets/weather/fog.json | 2 +- res/shaders/clouds.glslf | 31 ++++++++++++++++ res/shaders/clouds.glslv | 49 ++++++++++++++++++++++++++ src/graphics/render/CloudsRenderer.cpp | 23 ++++++++---- src/graphics/render/CloudsRenderer.hpp | 14 +++++--- src/graphics/render/WorldRenderer.cpp | 15 ++++++-- 8 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 res/shaders/clouds.glslf create mode 100644 res/shaders/clouds.glslv diff --git a/res/preload.json b/res/preload.json index 558fb1f0e..00948068e 100644 --- a/res/preload.json +++ b/res/preload.json @@ -8,7 +8,8 @@ "translucent", "background", "skybox_gen", - "shadows" + "shadows", + "clouds" ], "post-effects": [ "default", diff --git a/res/presets/weather/cloudy.json b/res/presets/weather/cloudy.json index 645b0a287..c9af2f0d6 100644 --- a/res/presets/weather/cloudy.json +++ b/res/presets/weather/cloudy.json @@ -1,4 +1,4 @@ { - "clouds": 0.8 + "clouds": 1.0 } diff --git a/res/presets/weather/fog.json b/res/presets/weather/fog.json index e9c3a2b34..6f65685c8 100644 --- a/res/presets/weather/fog.json +++ b/res/presets/weather/fog.json @@ -2,5 +2,5 @@ "fog_opacity": 0.98, "fog_dencity": 3.6, "fog_curve": 0.25, - "clouds": 0.8 + "clouds": 0.9 } diff --git a/res/shaders/clouds.glslf b/res/shaders/clouds.glslf new file mode 100644 index 000000000..9b29ef110 --- /dev/null +++ b/res/shaders/clouds.glslf @@ -0,0 +1,31 @@ +layout (location = 0) out vec4 f_color; +layout (location = 1) out vec4 f_position; +layout (location = 2) out vec4 f_normal; +layout (location = 3) out vec4 f_emission; + +#include + +in vec4 a_torchLight; + +uniform sampler2D u_texture0; +uniform vec3 u_sunDir; + +// flags +uniform bool u_alphaClip; +uniform bool u_debugLights; +uniform bool u_debugNormals; +uniform vec4 u_tint; + +void main() { + f_color.rgb = max(a_torchLight.rgb, a_skyLight); + f_color.a = 1.0; + f_color *= u_tint; + +#ifndef ADVANCED_RENDER + vec3 fogColor = texture(u_skybox, a_dir).rgb; + f_color = mix(f_color, vec4(fogColor, 1.0), a_fog); +#endif + f_position = vec4(a_position, 1.0); + f_normal = vec4(a_normal, 1.0); + f_emission = vec4(vec3(a_emission), 1.0); +} diff --git a/res/shaders/clouds.glslv b/res/shaders/clouds.glslv new file mode 100644 index 000000000..6c36aa464 --- /dev/null +++ b/res/shaders/clouds.glslv @@ -0,0 +1,49 @@ +#include + +layout (location = 0) in vec3 v_position; +layout (location = 1) in vec2 v_texCoord; +layout (location = 2) in vec4 v_light; +layout (location = 3) in vec4 v_normal; + +#include +#include +#include +#include + +uniform float u_dayTime; + +out vec4 a_torchLight; + +void main() { + a_modelpos = u_model * vec4(v_position, 1.0f); + a_modelpos.x += sin(u_timer * 0.05 + a_modelpos.z * 0.002) * 1.5; + a_modelpos.z += sin(u_timer * 0.2 + a_modelpos.x * 0.002) * 1.5; + a_modelpos.y += sin(u_timer * 0.1 + (a_modelpos.x + a_modelpos.z) * 0.001) * 2.0; + + vec3 pos3d = a_modelpos.xyz - u_cameraPos; + + a_realnormal = v_normal.xyz * 2.0 - 1.0; + a_normal = calc_screen_normal(a_realnormal); + + a_torchLight = vec4(calc_torch_light( + v_light.rgb, a_realnormal, a_modelpos.xyz, u_torchlightColor, u_gamma + ), 1.0); + a_texCoord = v_texCoord; + + a_dir = a_modelpos.xyz - u_cameraPos; + vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime); + a_skyLight = skyLightColor.rgb*v_light.a; + + mat4 viewmodel = u_view * u_model; + a_distance = length(viewmodel * vec4(pos3d, 0.0)); + +#ifndef ADVANCED_RENDER + a_fog = calc_fog(length(viewmodel * vec4(pos3d * FOG_POS_SCALE, 0.0)) / 256.0); +#endif + + a_emission = v_normal.w; + + vec4 viewmodelpos = u_view * a_modelpos; + a_position = viewmodelpos.xyz; + gl_Position = u_proj * viewmodelpos; +} diff --git a/src/graphics/render/CloudsRenderer.cpp b/src/graphics/render/CloudsRenderer.cpp index 6b38ee215..71fc4496f 100755 --- a/src/graphics/render/CloudsRenderer.cpp +++ b/src/graphics/render/CloudsRenderer.cpp @@ -12,6 +12,7 @@ #include "maths/FastNoiseLite.h" #include "util/timeutil.hpp" #include "window/Camera.hpp" +#include "world/Weather.hpp" #include #include @@ -163,7 +164,7 @@ static inline constexpr int WIDTH = 256; static inline constexpr int DEPTH = 256; -CloudsRenderer::CloudsRenderer(const Assets& assets) : assets(assets) { +CloudsRenderer::CloudsRenderer() { VolumeRenderer volumeRenderer(1024 * 1024); for (int layer = 0; layer < 2; layer++) { @@ -216,7 +217,6 @@ CloudsRenderer::CloudsRenderer(const Assets& assets) : assets(assets) { CloudsMap map({w, h, d}, voxels); - timeutil::ScopeLogTimer log2(222); volumeRenderer.build(map); testMeshes[layer] = std::make_unique>(volumeRenderer.createMesh()); } @@ -224,9 +224,20 @@ CloudsRenderer::CloudsRenderer(const Assets& assets) : assets(assets) { CloudsRenderer::~CloudsRenderer() = default; -void CloudsRenderer::draw(float timer, float fogFactor, const Camera& camera) { - auto& shader = assets.require("main"); - assets.require("misc/blank").bind(); +void CloudsRenderer::draw( + Shader& shader, + const Weather& weather, + float timer, + float fogFactor, + const Camera& camera +) { + float clouds = weather.clouds(); + shader.uniform4f( + "u_tint", + glm::vec4( + 1.0f - clouds * 0.5, 1.0f - clouds * 0.45, 1.0f - clouds * 0.4, 1.0f + ) + ); float scale = 8; float totalWidth = WIDTH * scale; @@ -235,7 +246,7 @@ void CloudsRenderer::draw(float timer, float fogFactor, const Camera& camera) { int cellX = glm::floor(camera.position.x / totalWidth + 0.5f); int cellZ = glm::floor(camera.position.z / totalDepth + 0.5f); - float speed = 10.0f; + float speed = 4.0f; for (int i = 0; i < 2; i++) { float speedX = glm::sin(i * 0.3f + 0.4f) * speed / (i + 1); float speedZ = -glm::cos(i * 0.3f + 0.4f) * speed / (i + 1); diff --git a/src/graphics/render/CloudsRenderer.hpp b/src/graphics/render/CloudsRenderer.hpp index 2eec12e53..84c8e1c2b 100755 --- a/src/graphics/render/CloudsRenderer.hpp +++ b/src/graphics/render/CloudsRenderer.hpp @@ -4,16 +4,22 @@ #include -class Assets; class Camera; +class Shader; +struct Weather; class CloudsRenderer final { public: - CloudsRenderer(const Assets& assets); + CloudsRenderer(); ~CloudsRenderer(); - void draw(float timer, float fogFactor, const Camera& camera); + void draw( + Shader& shader, + const Weather& weather, + float timer, + float fogFactor, + const Camera& camera + ); private: - const Assets& assets; std::array>, 2> testMeshes; }; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index b00a6756e..1a7e8f055 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -117,7 +117,7 @@ WorldRenderer::WorldRenderer( ); shadowMapping = std::make_unique(level); debugLines = std::make_unique(level); - cloudsRenderer = std::make_unique(assets); + cloudsRenderer = std::make_unique(); } WorldRenderer::~WorldRenderer() = default; @@ -207,14 +207,17 @@ void WorldRenderer::renderOpaque( particles->render(camera); auto& shader = assets.require("main"); + auto& cloudsShader = assets.require("clouds"); auto& linesShader = assets.require("lines"); setupWorldShader(shader, camera, settings, fogFactor); chunksRenderer->drawChunks(camera, shader); - cloudsRenderer->draw(timer, fogFactor, camera); blockWraps->draw(ctx, player); + setupWorldShader(cloudsShader, camera, settings, fogFactor); + cloudsRenderer->draw(cloudsShader, weather, timer, fogFactor, camera); + if (hudVisible) { renderLines(camera, linesShader, ctx); } @@ -317,13 +320,19 @@ void WorldRenderer::renderFrame( auto& mainShader = assets.require("main"); auto& entityShader = assets.require("entity"); + auto& cloudsShader = assets.require("clouds"); auto& translucentShader = assets.require("translucent"); auto& deferredShader = assets.require("deferred_lighting").getShader(); const auto& settings = engine.getSettings(); Shader* affectedShaders[] { - &mainShader, &entityShader, &translucentShader, &deferredShader, nullptr + &mainShader, + &entityShader, + &cloudsShader, + &translucentShader, + &deferredShader, + nullptr }; refreshSettings(affectedShaders); From 1194379f99892403c2a279a77da651ef0a380e2a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 12 Feb 2026 18:34:32 +0300 Subject: [PATCH 06/12] add 'graphics.clouds-quality' setting --- res/layouts/pages/settings.xml | 2 +- res/layouts/pages/settings_graphics.xml.lua | 3 ++- src/graphics/render/CloudsRenderer.cpp | 5 +++-- src/graphics/render/CloudsRenderer.hpp | 3 ++- src/graphics/render/WorldRenderer.cpp | 9 +++++++-- src/io/settings_io.cpp | 1 + src/settings.hpp | 2 ++ 7 files changed, 18 insertions(+), 7 deletions(-) diff --git a/res/layouts/pages/settings.xml b/res/layouts/pages/settings.xml index 68a71aa04..cff91d395 100644 --- a/res/layouts/pages/settings.xml +++ b/res/layouts/pages/settings.xml @@ -1,4 +1,4 @@ - + diff --git a/res/layouts/pages/settings_graphics.xml.lua b/res/layouts/pages/settings_graphics.xml.lua index 3b7f7b13a..806f48d0c 100644 --- a/res/layouts/pages/settings_graphics.xml.lua +++ b/res/layouts/pages/settings_graphics.xml.lua @@ -39,11 +39,12 @@ function on_open() create_setting("chunks.load-distance", "Load Distance", 1) create_setting("chunks.load-speed", "Load Speed", 1) create_setting("graphics.fog-curve", "Fog Curve", 0.1) - + create_checkbox("graphics.backlight", "Backlight", "graphics.backlight.tooltip") create_checkbox("graphics.soft-lighting", "Soft lighting", "graphics.soft-lighting.tooltip") create_checkbox("graphics.dense-render", "Dense blocks render", "graphics.dense-render.tooltip") create_checkbox("graphics.advanced-render", "Advanced render", "graphics.advanced-render.tooltip") create_setting("graphics.ssao", "SSAO", 1, "", "graphics.ssao.tooltip") create_setting("graphics.shadows-quality", "Shadows quality", 1) + create_setting("graphics.clouds-quality", "Clouds quality", 1) end diff --git a/src/graphics/render/CloudsRenderer.cpp b/src/graphics/render/CloudsRenderer.cpp index 71fc4496f..eea8240be 100755 --- a/src/graphics/render/CloudsRenderer.cpp +++ b/src/graphics/render/CloudsRenderer.cpp @@ -229,7 +229,8 @@ void CloudsRenderer::draw( const Weather& weather, float timer, float fogFactor, - const Camera& camera + const Camera& camera, + int quality ) { float clouds = weather.clouds(); shader.uniform4f( @@ -247,7 +248,7 @@ void CloudsRenderer::draw( int cellZ = glm::floor(camera.position.z / totalDepth + 0.5f); float speed = 4.0f; - for (int i = 0; i < 2; i++) { + for (int i = 0; i < std::min(quality, testMeshes.size()); i++) { float speedX = glm::sin(i * 0.3f + 0.4f) * speed / (i + 1); float speedZ = -glm::cos(i * 0.3f + 0.4f) * speed / (i + 1); for (int x = -2; x <= 2; x++) { diff --git a/src/graphics/render/CloudsRenderer.hpp b/src/graphics/render/CloudsRenderer.hpp index 84c8e1c2b..d5f379749 100755 --- a/src/graphics/render/CloudsRenderer.hpp +++ b/src/graphics/render/CloudsRenderer.hpp @@ -18,7 +18,8 @@ public: const Weather& weather, float timer, float fogFactor, - const Camera& camera + const Camera& camera, + int quality ); private: std::array>, 2> testMeshes; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 1a7e8f055..aef2b8674 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -215,8 +215,13 @@ void WorldRenderer::renderOpaque( chunksRenderer->drawChunks(camera, shader); blockWraps->draw(ctx, player); - setupWorldShader(cloudsShader, camera, settings, fogFactor); - cloudsRenderer->draw(cloudsShader, weather, timer, fogFactor, camera); + int cloudsQuality = settings.graphics.cloudsQuality.get(); + if (cloudsQuality > 0) { + setupWorldShader(cloudsShader, camera, settings, fogFactor); + cloudsRenderer->draw( + cloudsShader, weather, timer, fogFactor, camera, cloudsQuality + ); + } if (hudVisible) { renderLines(camera, linesShader, ctx); diff --git a/src/io/settings_io.cpp b/src/io/settings_io.cpp index 76285d733..27e08c93f 100644 --- a/src/io/settings_io.cpp +++ b/src/io/settings_io.cpp @@ -82,6 +82,7 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) { builder.add("shadows-quality", &settings.graphics.shadowsQuality); builder.add("dense-render-distance", &settings.graphics.denseRenderDistance); builder.add("soft-lighting", &settings.graphics.softLighting); + builder.add("clouds-quality", &settings.graphics.cloudsQuality); builder.addSection("ui"); builder.add("language", &settings.ui.language); diff --git a/src/settings.hpp b/src/settings.hpp index c98690023..171db217e 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -91,6 +91,8 @@ struct GraphicsSettings { IntegerSetting denseRenderDistance {56, 0, 10'000}; /// @brief Soft lighting for blocks FlagSetting softLighting {true}; + /// @brief Clouds quality level + IntegerSetting cloudsQuality {2, 0, 2}; }; struct PathfindingSettings { From e0374626152ebd8604aa110e54c6add2a68313a4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 12 Feb 2026 20:27:29 +0300 Subject: [PATCH 07/12] add 'sky_tint', 'clouds_tint', 'min_sky_light' weather parameters --- res/presets/weather/cloudy.json | 5 ++++- res/presets/weather/fog.json | 3 ++- res/presets/weather/rain.json | 5 ++++- res/presets/weather/snow.json | 5 ++++- res/presets/weather/thunder.json | 4 +++- res/shaders/clouds.glslv | 2 +- res/shaders/entity.glslv | 2 +- res/shaders/lib/constants.glsl | 1 - res/shaders/lib/sky.glsl | 4 ++-- res/shaders/lib/world_uniforms.glsl | 1 + res/shaders/main.glslv | 2 +- res/shaders/skybox_gen.glslf | 4 +++- res/shaders/translucent.glslv | 2 +- src/graphics/render/CloudsRenderer.cpp | 14 ++++---------- src/graphics/render/CloudsRenderer.hpp | 2 +- src/graphics/render/PrecipitationRenderer.cpp | 2 +- src/graphics/render/Skybox.cpp | 7 +++++-- src/graphics/render/Skybox.hpp | 3 ++- src/graphics/render/WorldRenderer.cpp | 15 ++++++++++++--- src/presets/WeatherPreset.cpp | 6 ++++++ src/presets/WeatherPreset.hpp | 9 +++++++++ src/world/Weather.hpp | 13 +++++++++++++ 22 files changed, 80 insertions(+), 31 deletions(-) diff --git a/res/presets/weather/cloudy.json b/res/presets/weather/cloudy.json index c9af2f0d6..5e11a8ac6 100644 --- a/res/presets/weather/cloudy.json +++ b/res/presets/weather/cloudy.json @@ -1,4 +1,7 @@ { - "clouds": 1.0 + "clouds": 1.0, + "clouds_tint": [0.6, 0.7, 0.8], + "sky_tint": [1.5, 1.5, 1.5], + "min_sky_light": [0.05, 0.07, 0.1] } diff --git a/res/presets/weather/fog.json b/res/presets/weather/fog.json index 6f65685c8..458283969 100644 --- a/res/presets/weather/fog.json +++ b/res/presets/weather/fog.json @@ -2,5 +2,6 @@ "fog_opacity": 0.98, "fog_dencity": 3.6, "fog_curve": 0.25, - "clouds": 0.9 + "clouds": 0.9, + "min_sky_light": [0.0, 0.0, 0.0] } diff --git a/res/presets/weather/rain.json b/res/presets/weather/rain.json index 184a80f0d..90e0512e9 100644 --- a/res/presets/weather/rain.json +++ b/res/presets/weather/rain.json @@ -19,5 +19,8 @@ "fog_opacity": 0.5, "fog_dencity": 1.7, "fog_curve": 0.5, - "clouds": 0.9 + "clouds": 0.96, + "sky_tint": [2.0, 2.0, 2.0], + "clouds_tint": [0.6, 0.7, 0.8], + "min_sky_light": [0.05, 0.07, 0.1] } diff --git a/res/presets/weather/snow.json b/res/presets/weather/snow.json index da80a8560..18763aa85 100644 --- a/res/presets/weather/snow.json +++ b/res/presets/weather/snow.json @@ -11,5 +11,8 @@ "fog_opacity": 0.8, "fog_dencity": 2.5, "fog_curve": 0.5, - "clouds": 0.5 + "clouds": 1.0, + "sky_tint": [1.9, 1.9, 1.9], + "clouds_tint": [0.7, 0.8, 0.9], + "min_sky_light": [0.05, 0.07, 0.1] } diff --git a/res/presets/weather/thunder.json b/res/presets/weather/thunder.json index d2a107f93..0a6f97c96 100644 --- a/res/presets/weather/thunder.json +++ b/res/presets/weather/thunder.json @@ -19,5 +19,7 @@ "fog_dencity": 2.0, "fog_curve": 0.5, "clouds": 0.7, - "thunder_rate": 0.1 + "thunder_rate": 0.1, + "clouds_tint": [0.3, 0.4, 0.5], + "min_sky_light": [0.0, 0.0, 0.0] } diff --git a/res/shaders/clouds.glslv b/res/shaders/clouds.glslv index 6c36aa464..0b9c17774 100644 --- a/res/shaders/clouds.glslv +++ b/res/shaders/clouds.glslv @@ -31,7 +31,7 @@ void main() { a_texCoord = v_texCoord; a_dir = a_modelpos.xyz - u_cameraPos; - vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime); + vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime, u_minSkyLight); a_skyLight = skyLightColor.rgb*v_light.a; mat4 viewmodel = u_view * u_model; diff --git a/res/shaders/entity.glslv b/res/shaders/entity.glslv index d50cd0075..8f534a6dc 100644 --- a/res/shaders/entity.glslv +++ b/res/shaders/entity.glslv @@ -28,7 +28,7 @@ void main() { a_texCoord = v_texCoord; a_dir = a_modelpos.xyz - u_cameraPos; - vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime); + vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime, u_minSkyLight); 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 1c2f4062b..a8a9a4bb3 100644 --- a/res/shaders/lib/constants.glsl +++ b/res/shaders/lib/constants.glsl @@ -10,7 +10,6 @@ // lighting #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 #define FOG_POS_SCALE vec3(1.0, 0.2, 1.0) diff --git a/res/shaders/lib/sky.glsl b/res/shaders/lib/sky.glsl index 97203286b..cfdbc3f2b 100644 --- a/res/shaders/lib/sky.glsl +++ b/res/shaders/lib/sky.glsl @@ -3,12 +3,12 @@ #include -vec3 pick_sky_color(samplerCube cubemap, float dayTime) { +vec3 pick_sky_color(samplerCube cubemap, float dayTime, vec3 minSkyLight) { 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); + skyLightColor = max(minSkyLight, skyLightColor); return skyLightColor; } diff --git a/res/shaders/lib/world_uniforms.glsl b/res/shaders/lib/world_uniforms.glsl index 20c8f529e..96dde0110 100644 --- a/res/shaders/lib/world_uniforms.glsl +++ b/res/shaders/lib/world_uniforms.glsl @@ -11,5 +11,6 @@ uniform float u_timer; uniform samplerCube u_skybox; uniform vec3 u_torchlightColor; uniform float u_torchlightDistance; +uniform vec3 u_minSkyLight; #endif // GLSL_WORLD_UNIFORMS_ diff --git a/res/shaders/main.glslv b/res/shaders/main.glslv index 8cc7a7637..08cc6469e 100644 --- a/res/shaders/main.glslv +++ b/res/shaders/main.glslv @@ -27,7 +27,7 @@ void main() { a_texCoord = v_texCoord; a_dir = a_modelpos.xyz - u_cameraPos; - vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime); + vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime, u_minSkyLight); a_skyLight = skyLightColor.rgb*v_light.a; mat4 viewmodel = u_view * u_model; diff --git a/res/shaders/skybox_gen.glslf b/res/shaders/skybox_gen.glslf index 583104ebb..b6f2b7ffb 100644 --- a/res/shaders/skybox_gen.glslf +++ b/res/shaders/skybox_gen.glslf @@ -241,6 +241,8 @@ uniform vec3 u_xaxis; uniform vec3 u_yaxis; uniform vec3 u_zaxis; uniform vec3 u_lightDir; +uniform vec3 u_tint; +uniform vec3 u_hightlight; uniform int u_quality; uniform float u_mie; uniform float u_fog; @@ -295,5 +297,5 @@ void main() { col = 1.0 - exp(-col); col = min(col, vec3(1.0)); // Output to screen - f_color = vec4(col, 1.0); + f_color = vec4(col * u_tint + u_hightlight, 1.0); } diff --git a/res/shaders/translucent.glslv b/res/shaders/translucent.glslv index 16cc8c4e3..d9d8031c3 100644 --- a/res/shaders/translucent.glslv +++ b/res/shaders/translucent.glslv @@ -27,7 +27,7 @@ void main() { a_texCoord = v_texCoord; a_dir = a_modelpos.xyz - u_cameraPos; - vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime); + vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime, u_minSkyLight); a_skyLight = skyLightColor.rgb*v_light.a; mat4 viewmodel = u_view * u_model; diff --git a/src/graphics/render/CloudsRenderer.cpp b/src/graphics/render/CloudsRenderer.cpp index eea8240be..5253004f6 100755 --- a/src/graphics/render/CloudsRenderer.cpp +++ b/src/graphics/render/CloudsRenderer.cpp @@ -218,7 +218,7 @@ CloudsRenderer::CloudsRenderer() { CloudsMap map({w, h, d}, voxels); volumeRenderer.build(map); - testMeshes[layer] = std::make_unique>(volumeRenderer.createMesh()); + meshes[layer] = std::make_unique>(volumeRenderer.createMesh()); } } @@ -232,13 +232,7 @@ void CloudsRenderer::draw( const Camera& camera, int quality ) { - float clouds = weather.clouds(); - shader.uniform4f( - "u_tint", - glm::vec4( - 1.0f - clouds * 0.5, 1.0f - clouds * 0.45, 1.0f - clouds * 0.4, 1.0f - ) - ); + shader.uniform4f("u_tint", glm::vec4(weather.cloudsTint(), 1.0f)); float scale = 8; float totalWidth = WIDTH * scale; @@ -248,7 +242,7 @@ void CloudsRenderer::draw( int cellZ = glm::floor(camera.position.z / totalDepth + 0.5f); float speed = 4.0f; - for (int i = 0; i < std::min(quality, testMeshes.size()); i++) { + for (int i = 0; i < std::min(quality, meshes.size()); i++) { float speedX = glm::sin(i * 0.3f + 0.4f) * speed / (i + 1); float speedZ = -glm::cos(i * 0.3f + 0.4f) * speed / (i + 1); for (int x = -2; x <= 2; x++) { @@ -268,7 +262,7 @@ void CloudsRenderer::draw( shader.uniform1f("u_fogFactor", fogFactor * 0.03f); shader.uniform1f("u_fogCurve", 0.7f - 0.3f); shader.uniformMatrix("u_model", matrix); - testMeshes[i]->draw(); + meshes[i]->draw(); } } } diff --git a/src/graphics/render/CloudsRenderer.hpp b/src/graphics/render/CloudsRenderer.hpp index d5f379749..736acebd9 100755 --- a/src/graphics/render/CloudsRenderer.hpp +++ b/src/graphics/render/CloudsRenderer.hpp @@ -22,5 +22,5 @@ public: int quality ); private: - std::array>, 2> testMeshes; + std::array>, 2> meshes; }; diff --git a/src/graphics/render/PrecipitationRenderer.cpp b/src/graphics/render/PrecipitationRenderer.cpp index 341d94f88..c566a06d2 100644 --- a/src/graphics/render/PrecipitationRenderer.cpp +++ b/src/graphics/render/PrecipitationRenderer.cpp @@ -151,7 +151,7 @@ void PrecipitationRenderer::render( glm::cross(glm::vec3(0, 1, 0), face.right), FACE_SIZE, light_at(chunks, pos.x, y, pos.z), - glm::vec3(3.0f), + glm::vec3(1.0f), calc_uv(pos, face.right, timer, weather) ); } diff --git a/src/graphics/render/Skybox.cpp b/src/graphics/render/Skybox.cpp index b5c5191f2..c546c62e3 100644 --- a/src/graphics/render/Skybox.cpp +++ b/src/graphics/render/Skybox.cpp @@ -170,7 +170,7 @@ void Skybox::draw( drawStars(angle, opacity); } -void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality) { +void Skybox::refresh(const DrawContext& pctx, float t, float mie, const glm::vec3& tint, const glm::vec3& hightlight, uint quality) { frameid++; float dayTime = t; DrawContext ctx = pctx.sub(); @@ -201,11 +201,13 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality) shader.uniform1i("u_quality", quality); shader.uniform1f("u_mie", mie); + shader.uniform3f("u_tint", tint); + shader.uniform3f("u_hightlight", hightlight); shader.uniform1f("u_fog", mie - 1.0f); shader.uniform3f("u_lightDir", lightDir); shader.uniform1f("u_dayTime", dayTime); - if (glm::abs(mie-prevMie) + glm::abs(t-prevT) >= 0.01) { + if (glm::abs(mie-prevMie) + glm::abs(t-prevT) + glm::abs(prevHighlight.r - hightlight.r) >= 0.01) { for (uint face = 0; face < 6; face++) { refreshFace(face, cubemap); } @@ -215,6 +217,7 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality) } prevMie = mie; prevT = t; + prevHighlight = hightlight; cubemap->unbind(); glActiveTexture(GL_TEXTURE0); diff --git a/src/graphics/render/Skybox.hpp b/src/graphics/render/Skybox.hpp index f2b81876c..d2dde6f5e 100644 --- a/src/graphics/render/Skybox.hpp +++ b/src/graphics/render/Skybox.hpp @@ -49,6 +49,7 @@ class Skybox { float prevMie = -1.0f; float prevT = -1.0f; float sunAltitude = 45.0f; + glm::vec3 prevHighlight {1.0f}; glm::mat4 rotation; void drawStars(float angle, float opacity); @@ -68,7 +69,7 @@ public: float fog ); - void refresh(const DrawContext& pctx, float t, float mie, uint quality); + void refresh(const DrawContext& pctx, float t, float mie, const glm::vec3& tint, const glm::vec3& hightlight, uint quality); void bind() const; void unbind() const; bool isReady() const { diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index aef2b8674..4157eeb98 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -126,6 +126,7 @@ static void setup_weather(Shader& shader, const Weather& weather) { shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity()); shader.uniform1f("u_weatherFogDencity", weather.fogDencity()); shader.uniform1f("u_weatherFogCurve", weather.fogCurve()); + shader.uniform3f("u_minSkyLight", weather.minSkyLight()); } static void setup_camera(Shader& shader, const Camera& camera) { @@ -348,7 +349,15 @@ void WorldRenderer::renderFrame( clouds = glm::max(worldInfo.fog, clouds); float mie = 1.0f + glm::max(worldInfo.fog, clouds * 0.5f) * 2.0f; - skybox->refresh(pctx, worldInfo.daytime, mie, 4); + float random = rand() / static_cast(RAND_MAX); + skybox->refresh( + pctx, + worldInfo.daytime, + mie, + weather.skyTint(), + weather.highlight * random, + 4 + ); chunksRenderer->update(); @@ -402,15 +411,14 @@ void WorldRenderer::renderFrame( linesShader.uniformMatrix("u_projview", projView); lineBatch->flush(); + skybox->bind(); // Translucent blocks { auto sctx = ctx.sub(); sctx.setCullFace(true); - skybox->bind(); translucentShader.use(); setupWorldShader(translucentShader, camera, settings, fogFactor); chunksRenderer->drawSortedMeshes(camera, translucentShader); - skybox->unbind(); } // Weather effects @@ -429,6 +437,7 @@ void WorldRenderer::renderFrame( precipitation->render(camera, *weather); } } + skybox->unbind(); glBindFramebuffer(GL_FRAMEBUFFER, 0); } diff --git a/src/presets/WeatherPreset.cpp b/src/presets/WeatherPreset.cpp index 6ae0bae7e..3350e2d70 100644 --- a/src/presets/WeatherPreset.cpp +++ b/src/presets/WeatherPreset.cpp @@ -23,6 +23,9 @@ dv::value WeatherPreset::serialize() const { root["fog_opacity"] = fogOpacity; root["fog_dencity"] = fogDencity; root["fog_curve"] = fogCurve; + root["sky_tint"] = dv::to_value(skyTint); + root["clouds_tint"] = dv::to_value(cloudsTint); + root["min_sky_light"] = dv::to_value(minSkyLight); root["clouds"] = clouds; root["thunder_rate"] = thunderRate; @@ -51,6 +54,9 @@ void WeatherPreset::deserialize(const dv::value& src) { src.at("fog_opacity").get(fogOpacity); src.at("fog_dencity").get(fogDencity); src.at("fog_curve").get(fogCurve); + dv::get_vec(src, "sky_tint", skyTint); + dv::get_vec(src, "clouds_tint", cloudsTint); + dv::get_vec(src, "min_sky_light", minSkyLight); src.at("clouds").get(clouds); src.at("thunder_rate").get(thunderRate); } diff --git a/src/presets/WeatherPreset.hpp b/src/presets/WeatherPreset.hpp index e49d1f3cb..d66ef0f63 100644 --- a/src/presets/WeatherPreset.hpp +++ b/src/presets/WeatherPreset.hpp @@ -60,6 +60,15 @@ struct WeatherPreset : Serializable { /// @brief Weather effects intensity float intensity = 0.0f; + /// @brief Skybox color multiplier + glm::vec3 skyTint {1.0f, 1.0f, 1.0f}; + + /// @brief Clouds color multiplier + glm::vec3 cloudsTint {1.0f, 1.0f, 1.0f}; + + /// @brief Minimal sky-emitted lights (moon light) + glm::vec3 minSkyLight {0.2f, 0.25f, 0.33f}; + dv::value serialize() const override; void deserialize(const dv::value& src) override; }; diff --git a/src/world/Weather.hpp b/src/world/Weather.hpp index 6d8af0bb2..3084bf99e 100644 --- a/src/world/Weather.hpp +++ b/src/world/Weather.hpp @@ -12,6 +12,7 @@ struct Weather : Serializable { std::string nameB; float t = 1.0f; float speed = 0.0f; + glm::vec3 highlight {}; void update(float delta) { t += delta * speed; @@ -51,6 +52,18 @@ struct Weather : Serializable { return b.clouds * sqrtT + a.clouds * (1.0f - sqrtT); } + glm::vec3 skyTint() const { + return b.skyTint * t + a.skyTint * (1.0f - t); + } + + glm::vec3 cloudsTint() const { + return b.cloudsTint * t + a.cloudsTint * (1.0f - t); + } + + glm::vec3 minSkyLight() const { + return b.minSkyLight * t + a.minSkyLight * (1.0f - t); + } + dv::value serialize() const override; void deserialize(const dv::value& src) override; }; From c32029d5b3e964fcb01b1adbbbdc1d7379a67cf0 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 12 Feb 2026 22:13:17 +0300 Subject: [PATCH 08/12] update ru_RU.txt --- res/texts/ru_RU.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/res/texts/ru_RU.txt b/res/texts/ru_RU.txt index 1461f854c..fd35f10ef 100644 --- a/res/texts/ru_RU.txt +++ b/res/texts/ru_RU.txt @@ -110,6 +110,7 @@ settings.Windowed=Оконный settings.Borderless=Безрамочный settings.Microphone=Микрофон settings.microphone.None=Нет +settings.Clouds quality=Качество облаков # Управление chunks.reload=Перезагрузить Чанки From f0e5b54164b12626a56e3a3fb14add57dd7ddf67 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 13 Feb 2026 00:18:19 +0300 Subject: [PATCH 09/12] split clouds to segments --- src/graphics/render/CloudsRenderer.cpp | 162 ++++++++++++++++--------- src/graphics/render/CloudsRenderer.hpp | 18 ++- 2 files changed, 119 insertions(+), 61 deletions(-) diff --git a/src/graphics/render/CloudsRenderer.cpp b/src/graphics/render/CloudsRenderer.cpp index 5253004f6..749986ae5 100755 --- a/src/graphics/render/CloudsRenderer.cpp +++ b/src/graphics/render/CloudsRenderer.cpp @@ -1,3 +1,4 @@ +#define GLM_ENABLE_EXPERIMENTAL #include "CloudsRenderer.hpp" #include "assets/Assets.hpp" @@ -10,11 +11,13 @@ #include "io/io.hpp" #include "lighting/Lightmap.hpp" #include "maths/FastNoiseLite.h" +#include "maths/voxmaths.hpp" #include "util/timeutil.hpp" #include "window/Camera.hpp" #include "world/Weather.hpp" #include +#include #include static debug::Logger logger("clouds-render"); @@ -70,7 +73,6 @@ public: return; } } - logger.info() << "layer vertices: " << offset; } MeshData createMesh() const { @@ -160,70 +162,137 @@ private: } }; -static inline constexpr int WIDTH = 256; -static inline constexpr int DEPTH = 256; +static inline constexpr int MAP_SIZE = 512; CloudsRenderer::CloudsRenderer() { - VolumeRenderer volumeRenderer(1024 * 1024); + VolumeRenderer volumeRenderer(1024 * 512); + + int diameter = 4; + int segmentSize = MAP_SIZE / diameter; + + for (int layerid = 0; layerid < 2; layerid++) { + auto& layer = layers[layerid]; + layer.diameter = diameter; + layer.segmentSize = segmentSize; - for (int layer = 0; layer < 2; layer++) { fnl_state state = fnlCreateState(); - const int w = WIDTH; + const int w = MAP_SIZE; const int h = 8; - const int d = DEPTH; + const int d = MAP_SIZE; const int dd = d * 1.5; - state.seed = 5265 + layer * 3521; + state.seed = 5265 + layerid * 3521; float pi2 = glm::two_pi(); - float heightmap[w * dd]; + auto heightmap = std::make_unique(w * dd); for (int lz = 0; lz < dd; lz++) { for (int lx = 0; lx < w; lx++) { float x = glm::sin(lx / static_cast(w) * pi2) * w / pi2; float y = -glm::cos(lx / static_cast(w) * pi2) * w / pi2; float z = lz; float s = 1.5f; - auto n = fnlGetNoise3D(&state, x * s, y * s, z * s); - n += fnlGetNoise3D(&state, x * s + fnlGetNoise2D(&state, x * s * 4 + 2, z * s * 4) * 0.25f, y * s, z * 3.0f) * 0.5f; + auto n = fnlGetNoise3D(&state, x * s * 0.7, y * s, z * s * 0.7); + n += fnlGetNoise3D(&state, x * s + fnlGetNoise2D(&state, x * s * 4 + 2, z * s * 4) * 2.0f, y * s, z * 3.0f) * 0.5f; n += fnlGetNoise3D(&state, x * s * 2, y * s * 2, z * s * 2) * 0.25f; n += fnlGetNoise3D(&state, x * s * 4, y * s * 4, z * s * 4) * 0.125f * 2; n += fnlGetNoise3D(&state, x * s * 8, y * s * 8, z * s * 8) * 0.125f * 0.5f * 2; n += fnlGetNoise3D(&state, x * s * 16, y * s * 16, z * s * 16) * 0.125f * 0.25f * 3; n = glm::max(0.0f, n); - // n = n * n * 2.0f; - n -= 0.2f + layer * 0.5f; - // n -= 0.5f; + n += -0.1f - layerid * 0.3f; heightmap[lz * w + lx] = n; } } - bool voxels[w * h * d]; - for (int y = 0; y < h; y++) { - for (int z = 0; z < d; z++) { - for (int x = 0; x < w; x++) { - float n = heightmap[z * w + x]; - if (z < d / 2) { - float t = z / static_cast(d / 2); - n = n * t + heightmap[(d + z) * w + x] * (1.0f - t); + bool voxels[segmentSize * h * segmentSize]; + for (int sz = 0; sz < diameter; sz++) { + for (int sx = 0; sx < diameter; sx++) { + for (int y = 0; y < h; y++) { + for (int z = 0; z < segmentSize; z++) { + for (int x = 0; x < segmentSize; x++) { + int gx = (sx * segmentSize) + x; + int gz = (sz * segmentSize) + z; + + float n = heightmap[gz * MAP_SIZE + gx]; + if (gz < MAP_SIZE / 2) { + float t = gz / static_cast(MAP_SIZE / 2); + n = n * t + heightmap[(MAP_SIZE + gz) * MAP_SIZE + gx] * (1.0f - t); + } + bool solid = y <= n * h && y >= (0.5f - n * 0.5f) * h; + voxels[vox_index(x, y, z, segmentSize, segmentSize)] = solid; + } } - bool solid = y <= n * h && y >= (0.5f - n * 0.5f) * h; - voxels[vox_index(x, y, z, w, d)] = solid; } + + CloudsMap map({segmentSize, h, segmentSize}, voxels); + + volumeRenderer.build(map); + layer.meshes.push_back(std::make_unique>( + volumeRenderer.createMesh() + )); } } - - CloudsMap map({w, h, d}, voxels); - - volumeRenderer.build(map); - meshes[layer] = std::make_unique>(volumeRenderer.createMesh()); } } CloudsRenderer::~CloudsRenderer() = default; +void CloudsRenderer::draw( + Shader& shader, + Layer& layer, + const Camera& camera, + float timer, + int layerId +) { + float scale = 8; + int totalDiameter = layer.segmentSize * scale; + + int gcellX = floordiv(camera.position.x, totalDiameter); + int gcellZ = floordiv(camera.position.z, totalDiameter); + + float speed = 4.0f; + float speedX = glm::sin(layerId * 0.3f + 0.4f) * speed / (layerId + 1); + float speedZ = -glm::cos(layerId * 0.3f + 0.4f) * speed / (layerId + 1); + + int radius = 4; + + for (int x = -radius; x <= radius; x++) { + for (int z = -radius; z <= radius; z++) { + int lcellX = gcellX - floordiv(glm::floor(timer * speedX), totalDiameter); + int lcellZ = gcellZ - floordiv(glm::floor(timer * speedZ), totalDiameter); + glm::vec3 position( + -128 * scale + (x + gcellX) * layer.segmentSize * scale + + glm::mod(timer * speedX, static_cast(totalDiameter)), + 250 + layerId * 200, + -128 * scale + (z + gcellZ) * layer.segmentSize * scale + + glm::mod(timer * speedZ, static_cast(totalDiameter)) + ); + if (glm::distance2( + glm::vec2( + position.x + totalDiameter * 0.5f, + position.z + totalDiameter * 0.5f + ), + glm::vec2(camera.position.x, camera.position.z) + ) > 4e6) { + continue; + } + auto matrix = glm::mat4(1.0f); + matrix = glm::translate(matrix, position); + matrix = glm::scale(matrix, glm::vec3(scale, scale, scale)); + shader.uniformMatrix("u_model", matrix); + + int lx = (x + radius + lcellX) % layer.diameter; + int lz = (z + radius + lcellZ) % layer.diameter; + if (lx < 0) lx += layer.diameter; + if (lz < 0) lz += layer.diameter; + + layer.meshes[lz * layer.diameter + lx]->draw(); + } + } +} + void CloudsRenderer::draw( Shader& shader, const Weather& weather, @@ -233,37 +302,10 @@ void CloudsRenderer::draw( int quality ) { shader.uniform4f("u_tint", glm::vec4(weather.cloudsTint(), 1.0f)); + shader.uniform1f("u_fogFactor", fogFactor * 0.03f); + shader.uniform1f("u_fogCurve", 0.7f - 0.3f); - float scale = 8; - float totalWidth = WIDTH * scale; - float totalDepth = DEPTH * scale; - - int cellX = glm::floor(camera.position.x / totalWidth + 0.5f); - int cellZ = glm::floor(camera.position.z / totalDepth + 0.5f); - - float speed = 4.0f; - for (int i = 0; i < std::min(quality, meshes.size()); i++) { - float speedX = glm::sin(i * 0.3f + 0.4f) * speed / (i + 1); - float speedZ = -glm::cos(i * 0.3f + 0.4f) * speed / (i + 1); - for (int x = -2; x <= 2; x++) { - for (int z = -2; z <= 2; z++) { - auto matrix = glm::mat4(1.0f); - matrix = glm::translate( - matrix, - glm::vec3( - -128 * scale + (x + cellX) * WIDTH * scale + - glm::mod(timer * speedX, totalWidth), - 250 + i * 200, - -128 * scale + (z + cellZ) * DEPTH * scale + - glm::mod(timer * speedZ, totalDepth) - ) - ); - matrix = glm::scale(matrix, glm::vec3(scale, scale, scale)); - shader.uniform1f("u_fogFactor", fogFactor * 0.03f); - shader.uniform1f("u_fogCurve", 0.7f - 0.3f); - shader.uniformMatrix("u_model", matrix); - meshes[i]->draw(); - } - } + for (int i = 0; i < std::min(quality, layers.size()); i++) { + draw(shader, layers[i], camera, timer, i); } } diff --git a/src/graphics/render/CloudsRenderer.hpp b/src/graphics/render/CloudsRenderer.hpp index 736acebd9..22ea73185 100755 --- a/src/graphics/render/CloudsRenderer.hpp +++ b/src/graphics/render/CloudsRenderer.hpp @@ -2,7 +2,9 @@ #include "commons.hpp" +#include #include +#include class Camera; class Shader; @@ -22,5 +24,19 @@ public: int quality ); private: - std::array>, 2> meshes; + struct Layer { + int diameter; + int segmentSize; + std::vector>> meshes; + }; + + std::array layers; + + void draw( + Shader& shader, + Layer& layer, + const Camera& camera, + float timer, + int layerId + ); }; From 7f98dcf919e29f2f821255f8b02e3d70ddb8dc2c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 13 Feb 2026 00:27:52 +0300 Subject: [PATCH 10/12] add frustum culling to clouds segments --- src/graphics/render/CloudsRenderer.cpp | 12 ++++++++++-- src/graphics/render/CloudsRenderer.hpp | 4 +++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/graphics/render/CloudsRenderer.cpp b/src/graphics/render/CloudsRenderer.cpp index 749986ae5..a999b24e4 100755 --- a/src/graphics/render/CloudsRenderer.cpp +++ b/src/graphics/render/CloudsRenderer.cpp @@ -15,6 +15,7 @@ #include "util/timeutil.hpp" #include "window/Camera.hpp" #include "world/Weather.hpp" +#include "maths/FrustumCulling.hpp" #include #include @@ -240,8 +241,9 @@ CloudsRenderer::CloudsRenderer() { CloudsRenderer::~CloudsRenderer() = default; void CloudsRenderer::draw( - Shader& shader, Layer& layer, + Frustum& frustum, + Shader& shader, const Camera& camera, float timer, int layerId @@ -278,6 +280,9 @@ void CloudsRenderer::draw( ) > 4e6) { continue; } + if (!frustum.isBoxVisible(position, position + glm::vec3(layer.segmentSize * scale))) { + continue; + } auto matrix = glm::mat4(1.0f); matrix = glm::translate(matrix, position); matrix = glm::scale(matrix, glm::vec3(scale, scale, scale)); @@ -301,11 +306,14 @@ void CloudsRenderer::draw( const Camera& camera, int quality ) { + Frustum frustum; + frustum.update(camera.getProjView()); + shader.uniform4f("u_tint", glm::vec4(weather.cloudsTint(), 1.0f)); shader.uniform1f("u_fogFactor", fogFactor * 0.03f); shader.uniform1f("u_fogCurve", 0.7f - 0.3f); for (int i = 0; i < std::min(quality, layers.size()); i++) { - draw(shader, layers[i], camera, timer, i); + draw(layers[i], frustum, shader, camera, timer, i); } } diff --git a/src/graphics/render/CloudsRenderer.hpp b/src/graphics/render/CloudsRenderer.hpp index 22ea73185..cb2ee50bd 100755 --- a/src/graphics/render/CloudsRenderer.hpp +++ b/src/graphics/render/CloudsRenderer.hpp @@ -9,6 +9,7 @@ class Camera; class Shader; struct Weather; +class Frustum; class CloudsRenderer final { public: @@ -33,8 +34,9 @@ private: std::array layers; void draw( - Shader& shader, Layer& layer, + Frustum& frustum, + Shader& shader, const Camera& camera, float timer, int layerId From e3432b7c01fe7d6f4262c077b02ddbff09a0a4de Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 13 Feb 2026 00:41:36 +0300 Subject: [PATCH 11/12] cleanup --- src/graphics/render/CloudsRenderer.cpp | 116 ++++++++++++++----------- 1 file changed, 67 insertions(+), 49 deletions(-) diff --git a/src/graphics/render/CloudsRenderer.cpp b/src/graphics/render/CloudsRenderer.cpp index a999b24e4..e2047eb63 100755 --- a/src/graphics/render/CloudsRenderer.cpp +++ b/src/graphics/render/CloudsRenderer.cpp @@ -11,11 +11,10 @@ #include "io/io.hpp" #include "lighting/Lightmap.hpp" #include "maths/FastNoiseLite.h" +#include "maths/FrustumCulling.hpp" #include "maths/voxmaths.hpp" -#include "util/timeutil.hpp" #include "window/Camera.hpp" #include "world/Weather.hpp" -#include "maths/FrustumCulling.hpp" #include #include @@ -23,6 +22,10 @@ static debug::Logger logger("clouds-render"); +static inline constexpr int MAP_SIZE = 512; +static inline constexpr float CLOUD_VOXEL_SCALE = 8.0f; +static inline constexpr float CLOUDS_SPEED = 4.0f; + class CloudsMap { public: glm::ivec3 size; @@ -163,14 +166,69 @@ private: } }; -static inline constexpr int MAP_SIZE = 512; +static void generate_heightmap( + float* heightmap, fnl_state& state, int w, int dd, int layerid +) { + float pi2 = glm::two_pi(); + for (int lz = 0; lz < dd; lz++) { + for (int lx = 0; lx < w; lx++) { + float x = glm::sin(lx / static_cast(w) * pi2) * w / pi2; + float y = -glm::cos(lx / static_cast(w) * pi2) * w / pi2; + float z = lz; + float s = 1.5f; + auto n = fnlGetNoise3D(&state, x * s * 0.7, y * s, z * s * 0.7); + n += fnlGetNoise3D(&state, x * s + fnlGetNoise2D(&state, x * s * 4 + 2, z * s * 4) * 2.0f, y * s, z * 3.0f) * 0.5f; + n += fnlGetNoise3D(&state, x * s * 2, y * s * 2, z * s * 2) * 0.25f; + n += fnlGetNoise3D(&state, x * s * 4, y * s * 4, z * s * 4) * 0.125f * 2; + n += fnlGetNoise3D(&state, x * s * 8, y * s * 8, z * s * 8) * 0.125f * 0.5f * 2; + n += fnlGetNoise3D(&state, x * s * 16, y * s * 16, z * s * 16) * 0.125f * 0.25f * 3; + n = glm::max(0.0f, n); + n += -0.1f - layerid * 0.3f; + + heightmap[lz * w + lx] = n; + } + } +} + +static void sample_voxels( + bool* voxels, + const float* heightmap, + int height, + int segmentSize, + int segmentX, + int segmentZ +) { + for (int y = 0; y < height; y++) { + for (int z = 0; z < segmentSize; z++) { + for (int x = 0; x < segmentSize; x++) { + int gx = (segmentX * segmentSize) + x; + int gz = (segmentZ * segmentSize) + z; + + float n = heightmap[gz * MAP_SIZE + gx]; + if (gz < MAP_SIZE / 2) { + float t = gz / static_cast(MAP_SIZE / 2); + n = n * t + + heightmap[(MAP_SIZE + gz) * MAP_SIZE + gx] * (1.0f - t); + } + bool solid = y <= n * height && y >= (0.5f - n * 0.5f) * height; + voxels[vox_index(x, y, z, segmentSize, segmentSize)] = solid; + } + } + } +} CloudsRenderer::CloudsRenderer() { VolumeRenderer volumeRenderer(1024 * 512); - int diameter = 4; - int segmentSize = MAP_SIZE / diameter; + const int diameter = 4; + const int segmentSize = MAP_SIZE / diameter; + + const int w = MAP_SIZE; + const int h = 8; + const int d = MAP_SIZE; + const int dd = d * 1.5; + auto heightmap = std::make_unique(w * dd); for (int layerid = 0; layerid < 2; layerid++) { auto& layer = layers[layerid]; @@ -178,54 +236,14 @@ CloudsRenderer::CloudsRenderer() { layer.segmentSize = segmentSize; fnl_state state = fnlCreateState(); - const int w = MAP_SIZE; - const int h = 8; - const int d = MAP_SIZE; - const int dd = d * 1.5; - state.seed = 5265 + layerid * 3521; - float pi2 = glm::two_pi(); - - auto heightmap = std::make_unique(w * dd); - for (int lz = 0; lz < dd; lz++) { - for (int lx = 0; lx < w; lx++) { - float x = glm::sin(lx / static_cast(w) * pi2) * w / pi2; - float y = -glm::cos(lx / static_cast(w) * pi2) * w / pi2; - float z = lz; - float s = 1.5f; - auto n = fnlGetNoise3D(&state, x * s * 0.7, y * s, z * s * 0.7); - n += fnlGetNoise3D(&state, x * s + fnlGetNoise2D(&state, x * s * 4 + 2, z * s * 4) * 2.0f, y * s, z * 3.0f) * 0.5f; - n += fnlGetNoise3D(&state, x * s * 2, y * s * 2, z * s * 2) * 0.25f; - n += fnlGetNoise3D(&state, x * s * 4, y * s * 4, z * s * 4) * 0.125f * 2; - n += fnlGetNoise3D(&state, x * s * 8, y * s * 8, z * s * 8) * 0.125f * 0.5f * 2; - n += fnlGetNoise3D(&state, x * s * 16, y * s * 16, z * s * 16) * 0.125f * 0.25f * 3; - n = glm::max(0.0f, n); - n += -0.1f - layerid * 0.3f; - - heightmap[lz * w + lx] = n; - } - } + generate_heightmap(heightmap.get(), state, w, dd, layerid); bool voxels[segmentSize * h * segmentSize]; for (int sz = 0; sz < diameter; sz++) { for (int sx = 0; sx < diameter; sx++) { - for (int y = 0; y < h; y++) { - for (int z = 0; z < segmentSize; z++) { - for (int x = 0; x < segmentSize; x++) { - int gx = (sx * segmentSize) + x; - int gz = (sz * segmentSize) + z; - - float n = heightmap[gz * MAP_SIZE + gx]; - if (gz < MAP_SIZE / 2) { - float t = gz / static_cast(MAP_SIZE / 2); - n = n * t + heightmap[(MAP_SIZE + gz) * MAP_SIZE + gx] * (1.0f - t); - } - bool solid = y <= n * h && y >= (0.5f - n * 0.5f) * h; - voxels[vox_index(x, y, z, segmentSize, segmentSize)] = solid; - } - } - } + sample_voxels(voxels, heightmap.get(), h, segmentSize, sx, sz); CloudsMap map({segmentSize, h, segmentSize}, voxels); @@ -248,13 +266,13 @@ void CloudsRenderer::draw( float timer, int layerId ) { - float scale = 8; + float scale = CLOUD_VOXEL_SCALE; int totalDiameter = layer.segmentSize * scale; int gcellX = floordiv(camera.position.x, totalDiameter); int gcellZ = floordiv(camera.position.z, totalDiameter); - float speed = 4.0f; + float speed = CLOUDS_SPEED; float speedX = glm::sin(layerId * 0.3f + 0.4f) * speed / (layerId + 1); float speedZ = -glm::cos(layerId * 0.3f + 0.4f) * speed / (layerId + 1); From 2c8675f6ccff2ef8f6110de7ce6404ee57d9b024 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 13 Feb 2026 01:44:42 +0300 Subject: [PATCH 12/12] revert "update workflows" This reverts commit ef16b01159daafb03c37353c254796c769c16212. --- .github/workflows/appimage.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows-clang.yml | 2 +- .github/workflows/windows.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index c67ef2fe0..92c9d9fd6 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main", "volume-renderer" ] + branches: [ "main", "dev" ] workflow_call: inputs: build_name: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 675966354..99466ddab 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main", "volume-renderer" ] + branches: [ "main", "dev" ] workflow_call: inputs: build_name: diff --git a/.github/workflows/windows-clang.yml b/.github/workflows/windows-clang.yml index 384364368..af0b060ae 100644 --- a/.github/workflows/windows-clang.yml +++ b/.github/workflows/windows-clang.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main", "volume-renderer" ] + branches: [ "main", "dev" ] workflow_call: inputs: build_name: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 5f95321c7..d13479cd3 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main", "volume-renderer" ] + branches: [ "main", "dev" ] jobs: build-windows: