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;