From bbd13f18259d9fe5c7732af9148488c840ccf418 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 2 Nov 2024 19:37:39 +0300 Subject: [PATCH 01/32] add MainBatch & refactor ModelBatch, Batch3D --- src/graphics/core/Batch3D.cpp | 26 +++++++------ src/graphics/core/Batch3D.hpp | 24 ++++++------ src/graphics/render/MainBatch.cpp | 61 ++++++++++++++++++++++++++++++ src/graphics/render/MainBatch.hpp | 61 ++++++++++++++++++++++++++++++ src/graphics/render/ModelBatch.cpp | 58 ++++------------------------ src/graphics/render/ModelBatch.hpp | 39 ++----------------- 6 files changed, 159 insertions(+), 110 deletions(-) create mode 100644 src/graphics/render/MainBatch.cpp create mode 100644 src/graphics/render/MainBatch.hpp diff --git a/src/graphics/core/Batch3D.cpp b/src/graphics/core/Batch3D.cpp index dd6261e5..41db4294 100644 --- a/src/graphics/core/Batch3D.cpp +++ b/src/graphics/core/Batch3D.cpp @@ -118,12 +118,12 @@ void Batch3D::texture(const Texture* new_texture){ } void Batch3D::sprite( - glm::vec3 pos, - glm::vec3 up, - glm::vec3 right, + const glm::vec3& pos, + const glm::vec3& up, + const glm::vec3& right, float w, float h, const UVRegion& uv, - glm::vec4 color + const glm::vec4& color ){ const float r = color.r; const float g = color.g; @@ -175,7 +175,7 @@ inline glm::vec4 do_tint(float value) { } void Batch3D::xSprite( - float w, float h, const UVRegion& uv, const glm::vec4 tint, bool shading + float w, float h, const UVRegion& uv, const glm::vec4& tint, bool shading ) { face( glm::vec3(-w * 0.25f, 0.0f, -w * 0.25f), @@ -194,10 +194,10 @@ void Batch3D::xSprite( } void Batch3D::cube( - const glm::vec3 coord, - const glm::vec3 size, + const glm::vec3& coord, + const glm::vec3& size, const UVRegion(&texfaces)[6], - const glm::vec4 tint, + const glm::vec4& tint, bool shading ) { const glm::vec3 X(1.0f, 0.0f, 0.0f); @@ -237,22 +237,24 @@ void Batch3D::cube( } void Batch3D::blockCube( - const glm::vec3 size, + const glm::vec3& size, const UVRegion(&texfaces)[6], - const glm::vec4 tint, + const glm::vec4& tint, bool shading ) { cube((1.0f - size) * -0.5f, size, texfaces, tint, shading); } -void Batch3D::vertex(glm::vec3 coord, glm::vec2 uv, glm::vec4 tint) { +void Batch3D::vertex( + const glm::vec3& coord, const glm::vec2& uv, const glm::vec4& tint +) { if (index + B3D_VERTEX_SIZE >= capacity) { flush(); } vertex(coord, uv, tint.r, tint.g, tint.b, tint.a); } -void Batch3D::point(glm::vec3 coord, glm::vec4 tint) { +void Batch3D::point(const glm::vec3& coord, const glm::vec4& tint) { if (index + B3D_VERTEX_SIZE >= capacity) { flushPoints(); } diff --git a/src/graphics/core/Batch3D.hpp b/src/graphics/core/Batch3D.hpp index bd5f7b4e..672021fd 100644 --- a/src/graphics/core/Batch3D.hpp +++ b/src/graphics/core/Batch3D.hpp @@ -49,36 +49,36 @@ public: void begin(); void texture(const Texture* texture); void sprite( - glm::vec3 pos, - glm::vec3 up, - glm::vec3 right, + const glm::vec3& pos, + const glm::vec3& up, + const glm::vec3& right, float w, float h, const UVRegion& uv, - glm::vec4 tint + const glm::vec4& tint ); void xSprite( float w, float h, const UVRegion& uv, - const glm::vec4 tint, + const glm::vec4& tint, bool shading = true ); void cube( - const glm::vec3 coords, - const glm::vec3 size, + const glm::vec3& coords, + const glm::vec3& size, const UVRegion (&texfaces)[6], - const glm::vec4 tint, + const glm::vec4& tint, bool shading = true ); void blockCube( - const glm::vec3 size, + const glm::vec3& size, const UVRegion (&texfaces)[6], - const glm::vec4 tint, + const glm::vec4& tint, bool shading = true ); - void vertex(glm::vec3 pos, glm::vec2 uv, glm::vec4 tint); - void point(glm::vec3 pos, glm::vec4 tint); + void vertex(const glm::vec3& pos, const glm::vec2& uv, const glm::vec4& tint); + void point(const glm::vec3& pos, const glm::vec4& tint); void flush() override; void flushPoints(); }; diff --git a/src/graphics/render/MainBatch.cpp b/src/graphics/render/MainBatch.cpp new file mode 100644 index 00000000..11ef9835 --- /dev/null +++ b/src/graphics/render/MainBatch.cpp @@ -0,0 +1,61 @@ +#include "MainBatch.hpp" + +#include "graphics/core/Texture.hpp" +#include "graphics/core/Mesh.hpp" +#include "graphics/core/ImageData.hpp" + +#include "typedefs.hpp" + +static const vattr attrs[] = { + {3}, {2}, {3}, {1}, {0} +}; + +MainBatch::MainBatch(size_t capacity) + : buffer(std::make_unique(capacity * VERTEX_SIZE)), + capacity(capacity), + index(0), + mesh(std::make_unique(buffer.get(), 0, attrs)) { + + const ubyte pixels[] = { + 255, 255, 255, 255, + }; + ImageData image(ImageFormat::rgba8888, 1, 1, pixels); + blank = Texture::from(&image); +} + +MainBatch::~MainBatch() = default; + +void MainBatch::setTexture(const Texture* texture) { + if (texture == nullptr) { + texture = blank.get(); + } + if (texture != this->texture) { + flush(); + } + this->texture = texture; + region = UVRegion {0.0f, 0.0f, 1.0f, 1.0f}; +} + +void MainBatch::setTexture(const Texture* texture, const UVRegion& region) { + setTexture(texture); + this->region = region; +} + +void MainBatch::flush() { + if (index == 0) { + return; + } + if (texture == nullptr) { + texture = blank.get(); + } + texture->bind(); + mesh->reload(buffer.get(), index / VERTEX_SIZE); + mesh->draw(); + index = 0; +} + +void MainBatch::prepare(int vertices) { + if (index + VERTEX_SIZE * vertices > capacity * VERTEX_SIZE) { + flush(); + } +} diff --git a/src/graphics/render/MainBatch.hpp b/src/graphics/render/MainBatch.hpp new file mode 100644 index 00000000..9bc46366 --- /dev/null +++ b/src/graphics/render/MainBatch.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +#include "maths/UVRegion.hpp" + +class Mesh; +class Texture; + +class MainBatch { + std::unique_ptr const buffer; + size_t const capacity; + size_t index; + + UVRegion region {0.0f, 0.0f, 1.0f, 1.0f}; + + std::unique_ptr mesh; + std::unique_ptr blank; + + const Texture* texture = nullptr; +public: + /// xyz, uv, color, compressed lights + static inline constexpr uint VERTEX_SIZE = 9; + + MainBatch(size_t capacity); + + ~MainBatch(); + + void prepare(int vertices); + void setTexture(const Texture* texture); + void setTexture(const Texture* texture, const UVRegion& region); + void flush(); + + inline void vertex( + glm::vec3 pos, glm::vec2 uv, glm::vec4 light, glm::vec3 tint + ) { + float* buffer = this->buffer.get(); + buffer[index++] = pos.x; + buffer[index++] = pos.y; + buffer[index++] = pos.z; + buffer[index++] = uv.x * region.getWidth() + region.u1; + buffer[index++] = uv.y * region.getHeight() + region.v1; + buffer[index++] = tint.x; + buffer[index++] = tint.y; + buffer[index++] = tint.z; + + union { + float floating; + uint32_t integer; + } compressed; + + compressed.integer = (static_cast(light.r * 255) & 0xff) << 24; + compressed.integer |= (static_cast(light.g * 255) & 0xff) << 16; + compressed.integer |= (static_cast(light.b * 255) & 0xff) << 8; + compressed.integer |= (static_cast(light.a * 255) & 0xff); + + buffer[index++] = compressed.floating; + } +}; diff --git a/src/graphics/render/ModelBatch.cpp b/src/graphics/render/ModelBatch.cpp index f6a1b58b..02615602 100644 --- a/src/graphics/render/ModelBatch.cpp +++ b/src/graphics/render/ModelBatch.cpp @@ -10,6 +10,7 @@ #include "voxels/Chunks.hpp" #include "lighting/Lightmap.hpp" #include "settings.hpp" +#include "MainBatch.hpp" #define GLM_ENABLE_EXPERIMENTAL #include @@ -18,13 +19,6 @@ #include -/// xyz, uv, color, compressed lights -inline constexpr uint VERTEX_SIZE = 9; - -static const vattr attrs[] = { - {3}, {2}, {3}, {1}, {0} -}; - inline constexpr glm::vec3 X(1, 0, 0); inline constexpr glm::vec3 Y(0, 1, 0); inline constexpr glm::vec3 Z(0, 0, 1); @@ -57,18 +51,10 @@ ModelBatch::ModelBatch( Chunks* chunks, const EngineSettings* settings ) - : buffer(std::make_unique(capacity * VERTEX_SIZE)), - capacity(capacity), - index(0), - mesh(std::make_unique(buffer.get(), 0, attrs)), + : batch(std::make_unique(capacity)), assets(assets), chunks(chunks), settings(settings) { - const ubyte pixels[] = { - 255, 255, 255, 255, - }; - ImageData image(ImageFormat::rgba8888, 1, 1, pixels); - blank = Texture::from(&image); } ModelBatch::~ModelBatch() = default; @@ -94,15 +80,13 @@ void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix, size_t vcount = mesh.vertices.size(); const auto& vertexData = mesh.vertices.data(); for (size_t i = 0; i < vcount / 3; i++) { - if (index + VERTEX_SIZE * 3 > capacity * VERTEX_SIZE) { - flush(); - } + batch->prepare(3); for (size_t j = 0; j < 3; j++) { const auto vert = vertexData[i * 3 + j]; auto norm = rotation * vert.normal; float d = glm::dot(norm, SUN_VECTOR); d = 0.8f + d * 0.2f; - vertex(matrix * glm::vec4(vert.coord, 1.0f), vert.uv, lights*d, tint); + batch->vertex(matrix * glm::vec4(vert.coord, 1.0f), vert.uv, lights*d, tint); } } } @@ -135,7 +119,7 @@ void ModelBatch::render() { backlight ); } - flush(); + batch->flush(); entries.clear(); } @@ -148,37 +132,11 @@ void ModelBatch::setTexture(const std::string& name, if (varTextures && name.at(0) == '$') { const auto& found = varTextures->find(name); if (found == varTextures->end()) { - return setTexture(nullptr); + return batch->setTexture(nullptr); } else { return setTexture(found->second, varTextures); } } - - auto textureRegion = util::get_texture_region(*assets, name, "blocks:notfound"); - setTexture(textureRegion.texture); - region = textureRegion.region; -} - -void ModelBatch::setTexture(const Texture* texture) { - if (texture == nullptr) { - texture = blank.get(); - } - if (texture != this->texture) { - flush(); - } - this->texture = texture; - region = UVRegion {0.0f, 0.0f, 1.0f, 1.0f}; -} - -void ModelBatch::flush() { - if (index == 0) { - return; - } - if (texture == nullptr) { - texture = blank.get(); - } - texture->bind(); - mesh->reload(buffer.get(), index / VERTEX_SIZE); - mesh->draw(); - index = 0; + auto region = util::get_texture_region(*assets, name, "blocks:notfound"); + batch->setTexture(region.texture, region.region); } diff --git a/src/graphics/render/ModelBatch.hpp b/src/graphics/render/ModelBatch.hpp index 523eb300..5781baa1 100644 --- a/src/graphics/render/ModelBatch.hpp +++ b/src/graphics/render/ModelBatch.hpp @@ -13,6 +13,7 @@ class Texture; class Chunks; class Assets; struct EngineSettings; +class MainBatch; namespace model { struct Mesh; @@ -22,47 +23,15 @@ namespace model { using texture_names_map = std::unordered_map; class ModelBatch { - std::unique_ptr const buffer; - size_t const capacity; - size_t index; - - std::unique_ptr mesh; - std::unique_ptr blank; - Assets* assets; Chunks* chunks; - const Texture* texture = nullptr; - UVRegion region {0.0f, 0.0f, 1.0f, 1.0f}; + const EngineSettings* settings; glm::vec3 lightsOffset {}; static inline glm::vec3 SUN_VECTOR {0.411934f, 0.863868f, -0.279161f}; - inline void vertex( - glm::vec3 pos, glm::vec2 uv, glm::vec4 light, glm::vec3 tint - ) { - float* buffer = this->buffer.get(); - buffer[index++] = pos.x; - buffer[index++] = pos.y; - buffer[index++] = pos.z; - buffer[index++] = uv.x * region.getWidth() + region.u1; - buffer[index++] = uv.y * region.getHeight() + region.v1; - buffer[index++] = tint.x; - buffer[index++] = tint.y; - buffer[index++] = tint.z; - - union { - float floating; - uint32_t integer; - } compressed; - - compressed.integer = (static_cast(light.r * 255) & 0xff) << 24; - compressed.integer |= (static_cast(light.g * 255) & 0xff) << 16; - compressed.integer |= (static_cast(light.b * 255) & 0xff) << 8; - compressed.integer |= (static_cast(light.a * 255) & 0xff); - - buffer[index++] = compressed.floating; - } + std::unique_ptr batch; void draw(const model::Mesh& mesh, const glm::mat4& matrix, @@ -72,8 +41,6 @@ class ModelBatch { bool backlight); void setTexture(const std::string& name, const texture_names_map* varTextures); - void setTexture(const Texture* texture); - void flush(); struct DrawEntry { glm::mat4 matrix; From a0f885e26a4ac56c019935775c26f060bec4077b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 2 Nov 2024 19:41:35 +0300 Subject: [PATCH 02/32] add missing include --- src/graphics/render/MainBatch.cpp | 2 -- src/graphics/render/MainBatch.hpp | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/graphics/render/MainBatch.cpp b/src/graphics/render/MainBatch.cpp index 11ef9835..20572128 100644 --- a/src/graphics/render/MainBatch.cpp +++ b/src/graphics/render/MainBatch.cpp @@ -4,8 +4,6 @@ #include "graphics/core/Mesh.hpp" #include "graphics/core/ImageData.hpp" -#include "typedefs.hpp" - static const vattr attrs[] = { {3}, {2}, {3}, {1}, {0} }; diff --git a/src/graphics/render/MainBatch.hpp b/src/graphics/render/MainBatch.hpp index 9bc46366..346be269 100644 --- a/src/graphics/render/MainBatch.hpp +++ b/src/graphics/render/MainBatch.hpp @@ -4,6 +4,7 @@ #include #include +#include "typedefs.hpp" #include "maths/UVRegion.hpp" class Mesh; From 217176f74f83ce832c4cb771bbedd1dabfc8bbed Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 2 Nov 2024 20:40:40 +0300 Subject: [PATCH 03/32] add particles (WIP) --- src/graphics/render/Emitter.cpp | 53 ++++++++++++++++ src/graphics/render/Emitter.hpp | 56 +++++++++++++++++ src/graphics/render/MainBatch.cpp | 5 ++ src/graphics/render/MainBatch.hpp | 56 ++++++++++++++++- src/graphics/render/ParticlesRenderer.cpp | 75 +++++++++++++++++++++++ src/graphics/render/ParticlesRenderer.hpp | 22 +++++++ src/graphics/render/WorldRenderer.cpp | 12 +++- src/graphics/render/WorldRenderer.hpp | 5 ++ 8 files changed, 280 insertions(+), 4 deletions(-) create mode 100644 src/graphics/render/Emitter.cpp create mode 100644 src/graphics/render/Emitter.hpp create mode 100644 src/graphics/render/ParticlesRenderer.cpp create mode 100644 src/graphics/render/ParticlesRenderer.hpp diff --git a/src/graphics/render/Emitter.cpp b/src/graphics/render/Emitter.cpp new file mode 100644 index 00000000..5ccd62e1 --- /dev/null +++ b/src/graphics/render/Emitter.cpp @@ -0,0 +1,53 @@ +#include "Emitter.hpp" + +#include + +#include "graphics/core/Texture.hpp" + +Emitter::Emitter( + std::variant origin, + Particle prototype, + const Texture* texture, + float spawnInterval, + int count +) + : origin(std::move(origin)), + prototype(std::move(prototype)), + texture(texture), + spawnInterval(spawnInterval), + count(count) { +} + +const Texture* Emitter::getTexture() const { + return texture; +} + +void Emitter::update(float delta, std::vector& particles) { + if (count == 0) { + return; + } + glm::vec3 position {}; + if (auto staticPos = std::get_if(&origin)) { + position = *staticPos; + } else { + // TODO: implement for entity origin + } + timer += delta; + while (count && timer > spawnInterval) { + // spawn particle + Particle particle = prototype; + particle.position = position; + particle.velocity += glm::ballRand(1.0f) * explosion; + particles.push_back(std::move(particle)); + timer -= spawnInterval; + count--; + } +} + +void Emitter::setExplosion(const glm::vec3& magnitude) { + explosion = magnitude; +} + +bool Emitter::isDead() const { + return count == 0; +} diff --git a/src/graphics/render/Emitter.hpp b/src/graphics/render/Emitter.hpp new file mode 100644 index 00000000..aa2a27e5 --- /dev/null +++ b/src/graphics/render/Emitter.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include + +#include "typedefs.hpp" + +#include "maths/UVRegion.hpp" + +struct Particle { + glm::vec3 position; + glm::vec3 velocity; + float lifetime; + UVRegion region; +}; + +class Texture; + +class Emitter { + /// @brief Static position or entity + std::variant origin; + /// @brief Particle prototype + Particle prototype; + /// @brief Particle + const Texture* texture; + /// @brief Particles spawn interval + float spawnInterval; + /// @brief Number of particles should be spawned before emitter deactivation. + /// -1 is infinite + int count; + /// @brief Spawn timer used to determine number of particles + /// to spawn on update. May be innacurate. + float timer = 0.0f; + /// @brief Random velocity magnitude applying to spawned particles + glm::vec3 explosion {1.0f}; +public: + Emitter( + std::variant origin, + Particle prototype, + const Texture* texture, + float spawnInterval, + int count + ); + + const Texture* getTexture() const; + + /// @brief Update emitter and spawn particles + /// @param delta delta time + /// @param particles destination particles vector + void update(float delta, std::vector& particles); + + void setExplosion(const glm::vec3& magnitude); + + bool isDead() const; +}; diff --git a/src/graphics/render/MainBatch.cpp b/src/graphics/render/MainBatch.cpp index 20572128..786bd39c 100644 --- a/src/graphics/render/MainBatch.cpp +++ b/src/graphics/render/MainBatch.cpp @@ -52,6 +52,11 @@ void MainBatch::flush() { index = 0; } +void MainBatch::begin() { + texture = nullptr; + blank->bind(); +} + void MainBatch::prepare(int vertices) { if (index + VERTEX_SIZE * vertices > capacity * VERTEX_SIZE) { flush(); diff --git a/src/graphics/render/MainBatch.hpp b/src/graphics/render/MainBatch.hpp index 346be269..4af077ed 100644 --- a/src/graphics/render/MainBatch.hpp +++ b/src/graphics/render/MainBatch.hpp @@ -29,13 +29,18 @@ public: ~MainBatch(); + void begin(); + void prepare(int vertices); void setTexture(const Texture* texture); void setTexture(const Texture* texture, const UVRegion& region); void flush(); inline void vertex( - glm::vec3 pos, glm::vec2 uv, glm::vec4 light, glm::vec3 tint + const glm::vec3& pos, + const glm::vec2& uv, + const glm::vec4& light, + const glm::vec3& tint ) { float* buffer = this->buffer.get(); buffer[index++] = pos.x; @@ -59,4 +64,53 @@ public: buffer[index++] = compressed.floating; } + + inline void quad( + const glm::vec3& pos, + const glm::vec3& right, + const glm::vec3& up, + const glm::vec2& size, + const glm::vec4& light, + const glm::vec3& tint, + const UVRegion& subregion + ) { + prepare(6); + vertex( + pos - right * size.x * 0.5f - up * size.y * 0.5f, + {subregion.u1, subregion.v1}, + light, + tint + ); + vertex( + pos + right * size.x * 0.5f - up * size.y * 0.5f, + {subregion.u2, subregion.v1}, + light, + tint + ); + vertex( + pos + right * size.x * 0.5f + up * size.y * 0.5f, + {subregion.u2, subregion.v2}, + light, + tint + ); + + vertex( + pos - right * size.x * 0.5f - up * size.y * 0.5f, + {subregion.u1, subregion.v1}, + light, + tint + ); + vertex( + pos + right * size.x * 0.5f + up * size.y * 0.5f, + {subregion.u2, subregion.v2}, + light, + tint + ); + vertex( + pos - right * size.x * 0.5f + up * size.y * 0.5f, + {subregion.u1, subregion.v2}, + light, + tint + ); + } }; diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp new file mode 100644 index 00000000..5b541672 --- /dev/null +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -0,0 +1,75 @@ +#include "ParticlesRenderer.hpp" + +#include "assets/Assets.hpp" +#include "graphics/core/Shader.hpp" +#include "graphics/core/Texture.hpp" +#include "graphics/render/MainBatch.hpp" +#include "window/Camera.hpp" + +ParticlesRenderer::ParticlesRenderer(const Assets& assets) + : batch(std::make_unique(1024)) { + + Emitter emitter(glm::vec3(0, 100, 0), Particle { + glm::vec3(), glm::vec3(), 5.0f, UVRegion(0,0,1,1) + }, assets.get("gui/error"), 0.003f, -1); + emitters.push_back(std::move(emitter)); +} + +ParticlesRenderer::~ParticlesRenderer() = default; + +void ParticlesRenderer::render( + const Assets& assets, const Camera& camera, float delta +) { + const auto& right = camera.right; + const auto& up = camera.up; + + batch->begin(); + for (auto& [texture, vec] : particles) { + batch->setTexture(texture); + + auto iter = vec.begin(); + while (iter != vec.end()) { + auto& particle = *iter; + + particle.position += particle.velocity * delta; + + batch->quad( + particle.position, + right, + up, + glm::vec2(0.3f), + glm::vec4(1), + glm::vec3(1.0f), + particle.region + ); + + particle.lifetime -= delta; + if (particle.lifetime <= 0.0f) { + iter = vec.erase(iter); + } else { + iter++; + } + } + } + batch->flush(); + + auto iter = emitters.begin(); + while (iter != emitters.end()) { + auto& emitter = *iter; + auto texture = emitter.getTexture(); + const auto& found = particles.find(texture); + std::vector* vec; + if (found == particles.end()) { + vec = &particles[texture]; + } else { + vec = &found->second; + } + emitter.update(delta, *vec); + + if (emitter.isDead()) { + iter = emitters.erase(iter); + } else { + iter++; + } + } +} diff --git a/src/graphics/render/ParticlesRenderer.hpp b/src/graphics/render/ParticlesRenderer.hpp new file mode 100644 index 00000000..bd910b55 --- /dev/null +++ b/src/graphics/render/ParticlesRenderer.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#include "Emitter.hpp" + +class Texture; +class Assets; +class Camera; +class MainBatch; + +class ParticlesRenderer { + std::unordered_map> particles; + std::vector emitters; + std::unique_ptr batch; +public: + ParticlesRenderer(const Assets& assets); + ~ParticlesRenderer(); + + void render(const Assets& assets, const Camera& camera, float delta); +}; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 4b13a23f..b36b03f3 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -40,6 +40,7 @@ #include "graphics/core/PostProcessing.hpp" #include "graphics/core/Shader.hpp" #include "graphics/core/Texture.hpp" +#include "ParticlesRenderer.hpp" #include "ChunksRenderer.hpp" #include "ModelBatch.hpp" #include "Skybox.hpp" @@ -56,9 +57,13 @@ WorldRenderer::WorldRenderer( frustumCulling(std::make_unique()), lineBatch(std::make_unique()), modelBatch(std::make_unique( - 20'000, engine->getAssets(), level->chunks.get(), + 20'000, + engine->getAssets(), + level->chunks.get(), &engine->getSettings() - )) { + )), + particles(std::make_unique(*engine->getAssets())) { + renderer = std::make_unique( level, frontend->getContentGfxCache(), &engine->getSettings() ); @@ -189,7 +194,7 @@ void WorldRenderer::setupWorldShader( } void WorldRenderer::renderLevel( - const DrawContext&, + const DrawContext& ctx, const Camera& camera, const EngineSettings& settings, float delta, @@ -211,6 +216,7 @@ void WorldRenderer::renderLevel( delta, pause ); + particles->render(*assets, camera, delta); modelBatch->render(); auto shader = assets->get("main"); diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index c5d7040e..d2ed639a 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -8,12 +8,15 @@ #include +#include "Emitter.hpp" + class Level; class Player; class Camera; class Batch3D; class LineBatch; class ChunksRenderer; +class ParticlesRenderer; class Shader; class Frustum; class Engine; @@ -40,6 +43,8 @@ class WorldRenderer { std::unique_ptr skybox; std::unique_ptr batch3d; std::unique_ptr modelBatch; + std::unique_ptr particles; + float timer = 0.0f; bool drawChunk(size_t index, const Camera& camera, Shader* shader, bool culling); From cee214754bfc4a3a224446590fdf829aa56a57d1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 2 Nov 2024 21:07:34 +0300 Subject: [PATCH 04/32] add particles stats to debug panel --- src/frontend/debug_panel.cpp | 7 +++++++ src/graphics/render/ParticlesRenderer.cpp | 14 ++++++++++++-- src/graphics/render/ParticlesRenderer.hpp | 3 +++ src/graphics/render/WorldRenderer.cpp | 4 ++-- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index c16dd5dd..fd3e4a3d 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -9,6 +9,7 @@ #include "graphics/ui/elements/TrackBar.hpp" #include "graphics/ui/elements/InputBindBox.hpp" #include "graphics/render/WorldRenderer.hpp" +#include "graphics/render/ParticlesRenderer.hpp" #include "logic/scripting/scripting.hpp" #include "objects/Player.hpp" #include "objects/Entities.hpp" @@ -83,6 +84,12 @@ std::shared_ptr create_debug_panel( bool culling = settings.graphics.frustumCulling.get(); return L"frustum-culling: "+std::wstring(culling ? L"on" : L"off"); })); + panel->add(create_label([=]() { + return L"particles: " + + std::to_wstring(ParticlesRenderer::visibleParticles) + + L" emitters: " + + std::to_wstring(ParticlesRenderer::aliveEmitters); + })); panel->add(create_label([=]() { return L"chunks: "+std::to_wstring(level->chunks->getChunksCount())+ L" visible: "+std::to_wstring(level->chunks->visible); diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index 5b541672..7f0b67f5 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -1,17 +1,22 @@ #include "ParticlesRenderer.hpp" #include "assets/Assets.hpp" +#include "assets/assets_util.hpp" #include "graphics/core/Shader.hpp" #include "graphics/core/Texture.hpp" #include "graphics/render/MainBatch.hpp" #include "window/Camera.hpp" +size_t ParticlesRenderer::visibleParticles = 0; +size_t ParticlesRenderer::aliveEmitters = 0; + ParticlesRenderer::ParticlesRenderer(const Assets& assets) : batch(std::make_unique(1024)) { + auto region = util::get_texture_region(assets, "blocks:grass_side", ""); Emitter emitter(glm::vec3(0, 100, 0), Particle { - glm::vec3(), glm::vec3(), 5.0f, UVRegion(0,0,1,1) - }, assets.get("gui/error"), 0.003f, -1); + glm::vec3(), glm::vec3(), 5.0f, region.region + },region.texture, 0.001f, -1); emitters.push_back(std::move(emitter)); } @@ -24,9 +29,14 @@ void ParticlesRenderer::render( const auto& up = camera.up; batch->begin(); + + aliveEmitters = emitters.size(); + visibleParticles = 0; for (auto& [texture, vec] : particles) { batch->setTexture(texture); + visibleParticles += vec.size(); + auto iter = vec.begin(); while (iter != vec.end()) { auto& particle = *iter; diff --git a/src/graphics/render/ParticlesRenderer.hpp b/src/graphics/render/ParticlesRenderer.hpp index bd910b55..c7bca3fb 100644 --- a/src/graphics/render/ParticlesRenderer.hpp +++ b/src/graphics/render/ParticlesRenderer.hpp @@ -19,4 +19,7 @@ public: ~ParticlesRenderer(); void render(const Assets& assets, const Camera& camera, float delta); + + static size_t visibleParticles; + static size_t aliveEmitters; }; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index b36b03f3..12a2c798 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -63,7 +63,7 @@ WorldRenderer::WorldRenderer( &engine->getSettings() )), particles(std::make_unique(*engine->getAssets())) { - + renderer = std::make_unique( level, frontend->getContentGfxCache(), &engine->getSettings() ); @@ -216,7 +216,7 @@ void WorldRenderer::renderLevel( delta, pause ); - particles->render(*assets, camera, delta); + particles->render(*assets, camera, delta * !pause); modelBatch->render(); auto shader = assets->get("main"); From 1974298c8243e01640f59aea77bc4032a7525c9b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 2 Nov 2024 22:15:26 +0300 Subject: [PATCH 05/32] update particles garbage collection scheme --- src/graphics/render/Emitter.cpp | 1 + src/graphics/render/Emitter.hpp | 12 ++++++ src/graphics/render/ParticlesRenderer.cpp | 46 ++++++++++++++--------- src/graphics/render/ParticlesRenderer.hpp | 7 +++- src/graphics/render/WorldRenderer.cpp | 5 ++- 5 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/graphics/render/Emitter.cpp b/src/graphics/render/Emitter.cpp index 5ccd62e1..58f9c809 100644 --- a/src/graphics/render/Emitter.cpp +++ b/src/graphics/render/Emitter.cpp @@ -16,6 +16,7 @@ Emitter::Emitter( texture(texture), spawnInterval(spawnInterval), count(count) { + this->prototype.emitter = this; } const Texture* Emitter::getTexture() const { diff --git a/src/graphics/render/Emitter.hpp b/src/graphics/render/Emitter.hpp index aa2a27e5..6a1dc06c 100644 --- a/src/graphics/render/Emitter.hpp +++ b/src/graphics/render/Emitter.hpp @@ -8,10 +8,19 @@ #include "maths/UVRegion.hpp" +class Emitter; + struct Particle { + /// @brief Pointer used to access common behaviour. + /// Emitter must be utilized after all related particles despawn. + Emitter* emitter; + /// @brief Global position glm::vec3 position; + /// @brief Linear velocity glm::vec3 velocity; + /// @brief Remaining life time float lifetime; + /// @brief UV region UVRegion region; }; @@ -43,6 +52,7 @@ public: int count ); + /// @return Emitter particles texture const Texture* getTexture() const; /// @brief Update emitter and spawn particles @@ -50,7 +60,9 @@ public: /// @param particles destination particles vector void update(float delta, std::vector& particles); + /// @brief Set initial random velocity magitude void setExplosion(const glm::vec3& magnitude); + /// @return true if the emitter has spawned all particles bool isDead() const; }; diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index 7f0b67f5..065eebb3 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -15,24 +15,24 @@ ParticlesRenderer::ParticlesRenderer(const Assets& assets) auto region = util::get_texture_region(assets, "blocks:grass_side", ""); Emitter emitter(glm::vec3(0, 100, 0), Particle { - glm::vec3(), glm::vec3(), 5.0f, region.region - },region.texture, 0.001f, -1); - emitters.push_back(std::move(emitter)); + nullptr, glm::vec3(), glm::vec3(), 5.0f, region.region + },region.texture, 0.001f, 1000); + emitters.push_back(std::make_unique(emitter)); } ParticlesRenderer::~ParticlesRenderer() = default; -void ParticlesRenderer::render( - const Assets& assets, const Camera& camera, float delta -) { +void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { const auto& right = camera.right; const auto& up = camera.up; - batch->begin(); - - aliveEmitters = emitters.size(); - visibleParticles = 0; + std::vector unusedTextures; + for (auto& [texture, vec] : particles) { + if (vec.empty()) { + unusedTextures.push_back(texture); + continue; + } batch->setTexture(texture); visibleParticles += vec.size(); @@ -62,24 +62,36 @@ void ParticlesRenderer::render( } } batch->flush(); + for (const auto& texture : unusedTextures) { + particles.erase(texture); + } +} + +void ParticlesRenderer::render(const Camera& camera, float delta) { + batch->begin(); + aliveEmitters = emitters.size(); + visibleParticles = 0; + + renderParticles(camera, delta); + auto iter = emitters.begin(); while (iter != emitters.end()) { - auto& emitter = *iter; + auto& emitter = **iter; auto texture = emitter.getTexture(); const auto& found = particles.find(texture); std::vector* vec; if (found == particles.end()) { + if (emitter.isDead()) { + // destruct Emitter only when there is no particles spawned by it + iter = emitters.erase(iter); + continue; + } vec = &particles[texture]; } else { vec = &found->second; } emitter.update(delta, *vec); - - if (emitter.isDead()) { - iter = emitters.erase(iter); - } else { - iter++; - } + iter++; } } diff --git a/src/graphics/render/ParticlesRenderer.hpp b/src/graphics/render/ParticlesRenderer.hpp index c7bca3fb..0ca55123 100644 --- a/src/graphics/render/ParticlesRenderer.hpp +++ b/src/graphics/render/ParticlesRenderer.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "Emitter.hpp" @@ -12,13 +13,15 @@ class MainBatch; class ParticlesRenderer { std::unordered_map> particles; - std::vector emitters; + std::vector> emitters; std::unique_ptr batch; + + void renderParticles(const Camera& camera, float delta); public: ParticlesRenderer(const Assets& assets); ~ParticlesRenderer(); - void render(const Assets& assets, const Camera& camera, float delta); + void render(const Camera& camera, float delta); static size_t visibleParticles; static size_t aliveEmitters; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 12a2c798..c1e85451 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -203,7 +203,8 @@ void WorldRenderer::renderLevel( auto assets = engine->getAssets(); bool culling = engine->getSettings().graphics.frustumCulling.get(); - float fogFactor = 15.0f / ((float)settings.chunks.loadDistance.get() - 2); + float fogFactor = + 15.0f / static_cast(settings.chunks.loadDistance.get() - 2); auto entityShader = assets->get("entity"); setupWorldShader(entityShader, camera, settings, fogFactor); @@ -216,7 +217,7 @@ void WorldRenderer::renderLevel( delta, pause ); - particles->render(*assets, camera, delta * !pause); + particles->render(camera, delta * !pause); modelBatch->render(); auto shader = assets->get("main"); From b944f257f75c12a9e35387c8317bd90cf0c6f065 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 Nov 2024 01:59:41 +0300 Subject: [PATCH 06/32] add particles collision detection --- src/graphics/render/Emitter.cpp | 4 ++- src/graphics/render/Emitter.hpp | 11 +++++++- src/graphics/render/ParticlesRenderer.cpp | 32 ++++++++++++++++------- src/graphics/render/ParticlesRenderer.hpp | 4 ++- src/graphics/render/WorldRenderer.cpp | 5 ++-- src/voxels/Chunks.cpp | 13 ++++----- src/voxels/Chunks.hpp | 11 ++++++-- 7 files changed, 58 insertions(+), 22 deletions(-) diff --git a/src/graphics/render/Emitter.cpp b/src/graphics/render/Emitter.cpp index 58f9c809..ae2cdc48 100644 --- a/src/graphics/render/Emitter.cpp +++ b/src/graphics/render/Emitter.cpp @@ -15,7 +15,8 @@ Emitter::Emitter( prototype(std::move(prototype)), texture(texture), spawnInterval(spawnInterval), - count(count) { + count(count), + behaviour() { this->prototype.emitter = this; } @@ -37,6 +38,7 @@ void Emitter::update(float delta, std::vector& particles) { while (count && timer > spawnInterval) { // spawn particle Particle particle = prototype; + particle.emitter = this; particle.position = position; particle.velocity += glm::ballRand(1.0f) * explosion; particles.push_back(std::move(particle)); diff --git a/src/graphics/render/Emitter.hpp b/src/graphics/render/Emitter.hpp index 6a1dc06c..9f6940a4 100644 --- a/src/graphics/render/Emitter.hpp +++ b/src/graphics/render/Emitter.hpp @@ -26,6 +26,11 @@ struct Particle { class Texture; +struct ParticleBehaviour { + bool collision = true; + glm::vec3 gravity {0.0f, -16.0f, 0.0f}; +}; + class Emitter { /// @brief Static position or entity std::variant origin; @@ -42,8 +47,10 @@ class Emitter { /// to spawn on update. May be innacurate. float timer = 0.0f; /// @brief Random velocity magnitude applying to spawned particles - glm::vec3 explosion {1.0f}; + glm::vec3 explosion {8.0f}; public: + ParticleBehaviour behaviour; + Emitter( std::variant origin, Particle prototype, @@ -52,6 +59,8 @@ public: int count ); + explicit Emitter(const Emitter&) = delete; + /// @return Emitter particles texture const Texture* getTexture() const; diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index 065eebb3..913deb23 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -6,22 +6,38 @@ #include "graphics/core/Texture.hpp" #include "graphics/render/MainBatch.hpp" #include "window/Camera.hpp" +#include "world/Level.hpp" +#include "voxels/Chunks.hpp" size_t ParticlesRenderer::visibleParticles = 0; size_t ParticlesRenderer::aliveEmitters = 0; -ParticlesRenderer::ParticlesRenderer(const Assets& assets) - : batch(std::make_unique(1024)) { +ParticlesRenderer::ParticlesRenderer(const Assets& assets, const Level& level) + : batch(std::make_unique(1024)), level(level) { - auto region = util::get_texture_region(assets, "blocks:grass_side", ""); - Emitter emitter(glm::vec3(0, 100, 0), Particle { + auto region = util::get_texture_region(assets, "blocks:bazalt", ""); + emitters.push_back(std::make_unique(glm::vec3(0, 100, 0), Particle { nullptr, glm::vec3(), glm::vec3(), 5.0f, region.region - },region.texture, 0.001f, 1000); - emitters.push_back(std::make_unique(emitter)); + },region.texture, 0.002f, -1)); } ParticlesRenderer::~ParticlesRenderer() = default; +static inline void update_particle( + Particle& particle, float delta, const Chunks& chunks +) { + const auto& behave = particle.emitter->behaviour; + auto& pos = particle.position; + auto& vel = particle.velocity; + + vel += delta * behave.gravity; + if (behave.collision && chunks.isObstacleAt(pos + vel * delta)) { + vel *= 0.0f; + } + pos += vel * delta; + particle.lifetime -= delta; +} + void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { const auto& right = camera.right; const auto& up = camera.up; @@ -41,7 +57,7 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { while (iter != vec.end()) { auto& particle = *iter; - particle.position += particle.velocity * delta; + update_particle(particle, delta, *level.chunks); batch->quad( particle.position, @@ -52,8 +68,6 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { glm::vec3(1.0f), particle.region ); - - particle.lifetime -= delta; if (particle.lifetime <= 0.0f) { iter = vec.erase(iter); } else { diff --git a/src/graphics/render/ParticlesRenderer.hpp b/src/graphics/render/ParticlesRenderer.hpp index 0ca55123..6d1bbb94 100644 --- a/src/graphics/render/ParticlesRenderer.hpp +++ b/src/graphics/render/ParticlesRenderer.hpp @@ -10,15 +10,17 @@ class Texture; class Assets; class Camera; class MainBatch; +class Level; class ParticlesRenderer { + const Level& level; std::unordered_map> particles; std::vector> emitters; std::unique_ptr batch; void renderParticles(const Camera& camera, float delta); public: - ParticlesRenderer(const Assets& assets); + ParticlesRenderer(const Assets& assets, const Level& level); ~ParticlesRenderer(); void render(const Camera& camera, float delta); diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index c1e85451..ef579d7c 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -62,8 +62,9 @@ WorldRenderer::WorldRenderer( level->chunks.get(), &engine->getSettings() )), - particles(std::make_unique(*engine->getAssets())) { - + particles(std::make_unique( + *engine->getAssets(), *frontend->getLevel() + )) { renderer = std::make_unique( level, frontend->getContentGfxCache(), &engine->getSettings() ); diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index dbd2e17e..70c73140 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -61,10 +61,10 @@ voxel* Chunks::get(int32_t x, int32_t y, int32_t z) const { return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; } -const AABB* Chunks::isObstacleAt(float x, float y, float z) { - int ix = floor(x); - int iy = floor(y); - int iz = floor(z); +const AABB* Chunks::isObstacleAt(float x, float y, float z) const { + int ix = std::floor(x); + int iy = std::floor(y); + int iz = std::floor(z); voxel* v = get(ix, iy, iz); if (v == nullptr) { if (iy >= CHUNK_H) { @@ -172,8 +172,9 @@ Chunk* Chunks::getChunk(int x, int z) { } glm::ivec3 Chunks::seekOrigin( - glm::ivec3 pos, const Block& def, blockstate state -) { + const glm::ivec3& srcpos, const Block& def, blockstate state +) const { + auto pos = srcpos; const auto& rotation = def.rotations.variants[state.rotation]; auto segment = state.segment; while (true) { diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index 6e73193a..a1131f1b 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -69,7 +69,9 @@ public: /// @param def segment block definition /// @param state segment block state /// @return origin block position or `pos` if block is not extended - glm::ivec3 seekOrigin(glm::ivec3 pos, const Block& def, blockstate state); + glm::ivec3 seekOrigin( + const glm::ivec3& pos, const Block& def, blockstate state + ) const; /// @brief Check if required zone is replaceable /// @param def definition of the block that requires a replaceable zone @@ -97,7 +99,12 @@ public: glm::vec3 rayCastToObstacle(glm::vec3 start, glm::vec3 dir, float maxDist); - const AABB* isObstacleAt(float x, float y, float z); + const AABB* isObstacleAt(float x, float y, float z) const; + + const AABB* isObstacleAt(const glm::vec3& pos) const { + return isObstacleAt(pos.x, pos.y, pos.z); + } + bool isSolidBlock(int32_t x, int32_t y, int32_t z); bool isReplaceableBlock(int32_t x, int32_t y, int32_t z); bool isObstacleBlock(int32_t x, int32_t y, int32_t z); From 7abb1a88c785bea7ba72d23352954233724ba5c0 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 Nov 2024 02:23:37 +0300 Subject: [PATCH 07/32] add particles lighting --- src/graphics/render/Emitter.cpp | 5 +++- src/graphics/render/Emitter.hpp | 2 ++ src/graphics/render/MainBatch.cpp | 18 ++++++++++++++ src/graphics/render/MainBatch.hpp | 5 ++++ src/graphics/render/ModelBatch.cpp | 12 +-------- src/graphics/render/ParticlesRenderer.cpp | 30 +++++++++++++++++------ src/graphics/render/ParticlesRenderer.hpp | 8 +++++- src/graphics/render/WorldRenderer.cpp | 4 ++- src/voxels/Chunks.cpp | 4 +-- src/voxels/Chunks.hpp | 4 +-- 10 files changed, 66 insertions(+), 26 deletions(-) diff --git a/src/graphics/render/Emitter.cpp b/src/graphics/render/Emitter.cpp index ae2cdc48..fd3ac978 100644 --- a/src/graphics/render/Emitter.cpp +++ b/src/graphics/render/Emitter.cpp @@ -39,11 +39,14 @@ void Emitter::update(float delta, std::vector& particles) { // spawn particle Particle particle = prototype; particle.emitter = this; + particle.random = rand(); particle.position = position; particle.velocity += glm::ballRand(1.0f) * explosion; particles.push_back(std::move(particle)); timer -= spawnInterval; - count--; + if (count > 0) { + count--; + } } } diff --git a/src/graphics/render/Emitter.hpp b/src/graphics/render/Emitter.hpp index 9f6940a4..2582e969 100644 --- a/src/graphics/render/Emitter.hpp +++ b/src/graphics/render/Emitter.hpp @@ -14,6 +14,7 @@ struct Particle { /// @brief Pointer used to access common behaviour. /// Emitter must be utilized after all related particles despawn. Emitter* emitter; + int random; /// @brief Global position glm::vec3 position; /// @brief Linear velocity @@ -28,6 +29,7 @@ class Texture; struct ParticleBehaviour { bool collision = true; + bool lighting = true; glm::vec3 gravity {0.0f, -16.0f, 0.0f}; }; diff --git a/src/graphics/render/MainBatch.cpp b/src/graphics/render/MainBatch.cpp index 786bd39c..c07a798a 100644 --- a/src/graphics/render/MainBatch.cpp +++ b/src/graphics/render/MainBatch.cpp @@ -3,6 +3,8 @@ #include "graphics/core/Texture.hpp" #include "graphics/core/Mesh.hpp" #include "graphics/core/ImageData.hpp" +#include "voxels/Chunks.hpp" +#include "voxels/Chunk.hpp" static const vattr attrs[] = { {3}, {2}, {3}, {1}, {0} @@ -62,3 +64,19 @@ void MainBatch::prepare(int vertices) { flush(); } } + +glm::vec4 MainBatch::sampleLight( + const glm::vec3& pos, const Chunks& chunks, bool backlight +) { + light_t light = chunks.getLight( + std::floor(pos.x), + std::floor(std::min(CHUNK_H-1.0f, pos.y)), + std::floor(pos.z)); + light_t minIntensity = backlight ? 1 : 0; + return glm::vec4( + glm::max(Lightmap::extract(light, 0), minIntensity) / 15.0f, + glm::max(Lightmap::extract(light, 1), minIntensity) / 15.0f, + glm::max(Lightmap::extract(light, 2), minIntensity) / 15.0f, + glm::max(Lightmap::extract(light, 3), minIntensity) / 15.0f + ); +} diff --git a/src/graphics/render/MainBatch.hpp b/src/graphics/render/MainBatch.hpp index 4af077ed..bb25e83b 100644 --- a/src/graphics/render/MainBatch.hpp +++ b/src/graphics/render/MainBatch.hpp @@ -9,6 +9,7 @@ class Mesh; class Texture; +class Chunks; class MainBatch { std::unique_ptr const buffer; @@ -36,6 +37,10 @@ public: void setTexture(const Texture* texture, const UVRegion& region); void flush(); + static glm::vec4 sampleLight( + const glm::vec3& pos, const Chunks& chunks, bool backlight + ); + inline void vertex( const glm::vec3& pos, const glm::vec2& uv, diff --git a/src/graphics/render/ModelBatch.cpp b/src/graphics/render/ModelBatch.cpp index 02615602..e222b349 100644 --- a/src/graphics/render/ModelBatch.cpp +++ b/src/graphics/render/ModelBatch.cpp @@ -65,17 +65,7 @@ void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix, bool backlight) { glm::vec3 gpos = matrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); gpos += lightsOffset; - light_t light = chunks->getLight( - std::floor(gpos.x), - std::floor(std::min(CHUNK_H-1.0f, gpos.y)), - std::floor(gpos.z)); - light_t minIntensity = backlight ? 1 : 0; - glm::vec4 lights( - glm::max(Lightmap::extract(light, 0), minIntensity) / 15.0f, - glm::max(Lightmap::extract(light, 1), minIntensity) / 15.0f, - glm::max(Lightmap::extract(light, 2), minIntensity) / 15.0f, - glm::max(Lightmap::extract(light, 3), minIntensity) / 15.0f - ); + glm::vec4 lights = MainBatch::sampleLight(gpos, *chunks, backlight); setTexture(mesh.texture, varTextures); size_t vcount = mesh.vertices.size(); const auto& vertexData = mesh.vertices.data(); diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index 913deb23..4036dd90 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -8,16 +8,20 @@ #include "window/Camera.hpp" #include "world/Level.hpp" #include "voxels/Chunks.hpp" +#include "settings.hpp" size_t ParticlesRenderer::visibleParticles = 0; size_t ParticlesRenderer::aliveEmitters = 0; -ParticlesRenderer::ParticlesRenderer(const Assets& assets, const Level& level) - : batch(std::make_unique(1024)), level(level) { - - auto region = util::get_texture_region(assets, "blocks:bazalt", ""); - emitters.push_back(std::make_unique(glm::vec3(0, 100, 0), Particle { - nullptr, glm::vec3(), glm::vec3(), 5.0f, region.region +ParticlesRenderer::ParticlesRenderer( + const Assets& assets, const Level& level, const GraphicsSettings* settings +) + : batch(std::make_unique(1024)), + level(level), + settings(settings) { + auto region = util::get_texture_region(assets, "blocks:grass_top", ""); + emitters.push_back(std::make_unique(glm::vec3(0, 80, 0), Particle { + nullptr, 0, glm::vec3(), glm::vec3(), 5.0f, region.region },region.texture, 0.002f, -1)); } @@ -42,6 +46,9 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { const auto& right = camera.right; const auto& up = camera.up; + const auto& chunks = *level.chunks; + bool backlight = settings->backlight.get(); + std::vector unusedTextures; for (auto& [texture, vec] : particles) { @@ -57,14 +64,21 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { while (iter != vec.end()) { auto& particle = *iter; - update_particle(particle, delta, *level.chunks); + update_particle(particle, delta, chunks); + glm::vec4 light(1, 1, 1, 0); + if (particle.emitter->behaviour.lighting) { + light = MainBatch::sampleLight( + particle.position, chunks, backlight + ); + light *= 0.7f + (particle.random % 300) * 0.001f; + } batch->quad( particle.position, right, up, glm::vec2(0.3f), - glm::vec4(1), + light, glm::vec3(1.0f), particle.region ); diff --git a/src/graphics/render/ParticlesRenderer.hpp b/src/graphics/render/ParticlesRenderer.hpp index 6d1bbb94..ca7906da 100644 --- a/src/graphics/render/ParticlesRenderer.hpp +++ b/src/graphics/render/ParticlesRenderer.hpp @@ -11,16 +11,22 @@ class Assets; class Camera; class MainBatch; class Level; +struct GraphicsSettings; class ParticlesRenderer { const Level& level; + const GraphicsSettings* settings; std::unordered_map> particles; std::vector> emitters; std::unique_ptr batch; void renderParticles(const Camera& camera, float delta); public: - ParticlesRenderer(const Assets& assets, const Level& level); + ParticlesRenderer( + const Assets& assets, + const Level& level, + const GraphicsSettings* settings + ); ~ParticlesRenderer(); void render(const Camera& camera, float delta); diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index ef579d7c..730125ad 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -63,7 +63,9 @@ WorldRenderer::WorldRenderer( &engine->getSettings() )), particles(std::make_unique( - *engine->getAssets(), *frontend->getLevel() + *engine->getAssets(), + *frontend->getLevel(), + &engine->getSettings().graphics )) { renderer = std::make_unique( level, frontend->getContentGfxCache(), &engine->getSettings() diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 70c73140..a380aeda 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -112,7 +112,7 @@ bool Chunks::isObstacleBlock(int32_t x, int32_t y, int32_t z) { return indices->blocks.get(v->id)->obstacle; //-V522 } -ubyte Chunks::getLight(int32_t x, int32_t y, int32_t z, int channel) { +ubyte Chunks::getLight(int32_t x, int32_t y, int32_t z, int channel) const { if (y < 0 || y >= CHUNK_H) { return 0; } @@ -132,7 +132,7 @@ ubyte Chunks::getLight(int32_t x, int32_t y, int32_t z, int channel) { return chunk->lightmap.get(lx, y, lz, channel); } -light_t Chunks::getLight(int32_t x, int32_t y, int32_t z) { +light_t Chunks::getLight(int32_t x, int32_t y, int32_t z) const { if (y < 0 || y >= CHUNK_H) { return 0; } diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index a1131f1b..98979423 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -60,8 +60,8 @@ public: return get(pos.x, pos.y, pos.z); } - light_t getLight(int32_t x, int32_t y, int32_t z); - ubyte getLight(int32_t x, int32_t y, int32_t z, int channel); + light_t getLight(int32_t x, int32_t y, int32_t z) const; + ubyte getLight(int32_t x, int32_t y, int32_t z, int channel) const; void set(int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state); /// @brief Seek for the extended block origin position From 082a87db4e666636b1edcac1b8f45d2bcaf50023 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 Nov 2024 02:26:11 +0300 Subject: [PATCH 08/32] update particles randomizer --- src/graphics/render/Emitter.cpp | 2 +- src/graphics/render/Emitter.hpp | 3 +++ src/graphics/render/ParticlesRenderer.cpp | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/graphics/render/Emitter.cpp b/src/graphics/render/Emitter.cpp index fd3ac978..e4fb9d47 100644 --- a/src/graphics/render/Emitter.cpp +++ b/src/graphics/render/Emitter.cpp @@ -39,7 +39,7 @@ void Emitter::update(float delta, std::vector& particles) { // spawn particle Particle particle = prototype; particle.emitter = this; - particle.random = rand(); + particle.random = random.rand32(); particle.position = position; particle.velocity += glm::ballRand(1.0f) * explosion; particles.push_back(std::move(particle)); diff --git a/src/graphics/render/Emitter.hpp b/src/graphics/render/Emitter.hpp index 2582e969..431d2e49 100644 --- a/src/graphics/render/Emitter.hpp +++ b/src/graphics/render/Emitter.hpp @@ -7,6 +7,7 @@ #include "typedefs.hpp" #include "maths/UVRegion.hpp" +#include "maths/util.hpp" class Emitter; @@ -50,6 +51,8 @@ class Emitter { float timer = 0.0f; /// @brief Random velocity magnitude applying to spawned particles glm::vec3 explosion {8.0f}; + + util::PseudoRandom random; public: ParticleBehaviour behaviour; diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index 4036dd90..e7b72c00 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -22,7 +22,7 @@ ParticlesRenderer::ParticlesRenderer( auto region = util::get_texture_region(assets, "blocks:grass_top", ""); emitters.push_back(std::make_unique(glm::vec3(0, 80, 0), Particle { nullptr, 0, glm::vec3(), glm::vec3(), 5.0f, region.region - },region.texture, 0.002f, -1)); + }, region.texture, 0.002f, -1)); } ParticlesRenderer::~ParticlesRenderer() = default; @@ -71,7 +71,7 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { light = MainBatch::sampleLight( particle.position, chunks, backlight ); - light *= 0.7f + (particle.random % 300) * 0.001f; + light *= 0.8f + (particle.random % 200) * 0.001f; } batch->quad( particle.position, From 6d63c712218dc1d1feffad41506c2c5b022fd3f3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 Nov 2024 12:45:46 +0300 Subject: [PATCH 09/32] add Emitter maxDistance --- src/graphics/render/Emitter.cpp | 23 +++++++++++++++++++++-- src/graphics/render/Emitter.hpp | 16 ++++++++++++---- src/graphics/render/ParticlesRenderer.cpp | 2 +- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/graphics/render/Emitter.cpp b/src/graphics/render/Emitter.cpp index e4fb9d47..ea64a93d 100644 --- a/src/graphics/render/Emitter.cpp +++ b/src/graphics/render/Emitter.cpp @@ -1,7 +1,10 @@ #include "Emitter.hpp" #include +#define GLM_ENABLE_EXPERIMENTAL +#include +#include "window/Camera.hpp" #include "graphics/core/Texture.hpp" Emitter::Emitter( @@ -24,8 +27,12 @@ const Texture* Emitter::getTexture() const { return texture; } -void Emitter::update(float delta, std::vector& particles) { - if (count == 0) { +void Emitter::update( + float delta, + const glm::vec3& cameraPosition, + std::vector& particles +) { + if (count == 0 || (count == -1 && spawnInterval < FLT_EPSILON)) { return; } glm::vec3 position {}; @@ -34,6 +41,18 @@ void Emitter::update(float delta, std::vector& particles) { } else { // TODO: implement for entity origin } + if (glm::distance2(position, cameraPosition) > maxDistance * maxDistance) { + if (count > 0) { + if (spawnInterval < FLT_EPSILON) { + count = 0; + return; + } + int skipped = timer / spawnInterval; + count = std::max(0, count - skipped); + timer -= skipped * spawnInterval; + } + return; + } timer += delta; while (count && timer > spawnInterval) { // spawn particle diff --git a/src/graphics/render/Emitter.hpp b/src/graphics/render/Emitter.hpp index 431d2e49..b8783ea8 100644 --- a/src/graphics/render/Emitter.hpp +++ b/src/graphics/render/Emitter.hpp @@ -15,6 +15,7 @@ struct Particle { /// @brief Pointer used to access common behaviour. /// Emitter must be utilized after all related particles despawn. Emitter* emitter; + /// @brief Some random integer for visuals configuration. int random; /// @brief Global position glm::vec3 position; @@ -44,15 +45,17 @@ class Emitter { /// @brief Particles spawn interval float spawnInterval; /// @brief Number of particles should be spawned before emitter deactivation. - /// -1 is infinite + /// -1 is infinite. int count; /// @brief Spawn timer used to determine number of particles /// to spawn on update. May be innacurate. float timer = 0.0f; - /// @brief Random velocity magnitude applying to spawned particles + /// @brief Random velocity magnitude applying to spawned particles. glm::vec3 explosion {8.0f}; + /// @brief Max distance of actually spawning particles. + float maxDistance = 32.0f; - util::PseudoRandom random; + util::PseudoRandom random; public: ParticleBehaviour behaviour; @@ -71,8 +74,13 @@ public: /// @brief Update emitter and spawn particles /// @param delta delta time + /// @param cameraPosition current camera global position /// @param particles destination particles vector - void update(float delta, std::vector& particles); + void update( + float delta, + const glm::vec3& cameraPosition, + std::vector& particles + ); /// @brief Set initial random velocity magitude void setExplosion(const glm::vec3& magnitude); diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index e7b72c00..f9c2e4d1 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -119,7 +119,7 @@ void ParticlesRenderer::render(const Camera& camera, float delta) { } else { vec = &found->second; } - emitter.update(delta, *vec); + emitter.update(delta, camera.position, *vec); iter++; } } From 83ddbda90b503d5bfb497bc762b57653354ef5be Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 Nov 2024 13:05:42 +0300 Subject: [PATCH 10/32] implement entity-bound emitters --- src/graphics/render/Emitter.cpp | 12 +++++++++--- src/graphics/render/Emitter.hpp | 3 +++ src/graphics/render/ParticlesRenderer.cpp | 13 +++++++++---- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/graphics/render/Emitter.cpp b/src/graphics/render/Emitter.cpp index ea64a93d..6a7dacd8 100644 --- a/src/graphics/render/Emitter.cpp +++ b/src/graphics/render/Emitter.cpp @@ -6,15 +6,19 @@ #include "window/Camera.hpp" #include "graphics/core/Texture.hpp" +#include "objects/Entities.hpp" +#include "world/Level.hpp" Emitter::Emitter( + const Level& level, std::variant origin, Particle prototype, const Texture* texture, float spawnInterval, int count ) - : origin(std::move(origin)), + : level(level), + origin(std::move(origin)), prototype(std::move(prototype)), texture(texture), spawnInterval(spawnInterval), @@ -38,8 +42,10 @@ void Emitter::update( glm::vec3 position {}; if (auto staticPos = std::get_if(&origin)) { position = *staticPos; - } else { - // TODO: implement for entity origin + } else if (auto entityId = std::get_if(&origin)) { + if (auto entity = level.entities->get(*entityId)) { + position = entity->getTransform().pos; + } } if (glm::distance2(position, cameraPosition) > maxDistance * maxDistance) { if (count > 0) { diff --git a/src/graphics/render/Emitter.hpp b/src/graphics/render/Emitter.hpp index b8783ea8..884ba11e 100644 --- a/src/graphics/render/Emitter.hpp +++ b/src/graphics/render/Emitter.hpp @@ -9,6 +9,7 @@ #include "maths/UVRegion.hpp" #include "maths/util.hpp" +class Level; class Emitter; struct Particle { @@ -36,6 +37,7 @@ struct ParticleBehaviour { }; class Emitter { + const Level& level; /// @brief Static position or entity std::variant origin; /// @brief Particle prototype @@ -60,6 +62,7 @@ public: ParticleBehaviour behaviour; Emitter( + const Level& level, std::variant origin, Particle prototype, const Texture* texture, diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index f9c2e4d1..0ad5c1d5 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -19,10 +19,15 @@ ParticlesRenderer::ParticlesRenderer( : batch(std::make_unique(1024)), level(level), settings(settings) { - auto region = util::get_texture_region(assets, "blocks:grass_top", ""); - emitters.push_back(std::make_unique(glm::vec3(0, 80, 0), Particle { - nullptr, 0, glm::vec3(), glm::vec3(), 5.0f, region.region - }, region.texture, 0.002f, -1)); + auto region = util::get_texture_region(assets, "blocks:grass_side", ""); + emitters.push_back(std::make_unique( + level, + glm::vec3(0, 80, 0), + Particle {nullptr, 0, glm::vec3(), glm::vec3(), 5.0f, region.region}, + region.texture, + 0.002f, + -1 + )); } ParticlesRenderer::~ParticlesRenderer() = default; From 9728c674fce77a35fc97cebef3fa7218f80b0f60 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 Nov 2024 14:25:29 +0300 Subject: [PATCH 11/32] add ParticlesPreset --- src/graphics/render/Emitter.cpp | 3 ++- src/graphics/render/Emitter.hpp | 11 ++--------- src/graphics/render/ParticlesRenderer.cpp | 8 ++++---- src/presets/ParticlesPreset.cpp | 20 ++++++++++++++++++++ src/presets/ParticlesPreset.hpp | 19 +++++++++++++++++++ 5 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 src/presets/ParticlesPreset.cpp create mode 100644 src/presets/ParticlesPreset.hpp diff --git a/src/graphics/render/Emitter.cpp b/src/graphics/render/Emitter.cpp index 6a7dacd8..7e4d55f7 100644 --- a/src/graphics/render/Emitter.cpp +++ b/src/graphics/render/Emitter.cpp @@ -23,7 +23,7 @@ Emitter::Emitter( texture(texture), spawnInterval(spawnInterval), count(count), - behaviour() { + preset() { this->prototype.emitter = this; } @@ -47,6 +47,7 @@ void Emitter::update( position = entity->getTransform().pos; } } + const float maxDistance = preset.maxDistance; if (glm::distance2(position, cameraPosition) > maxDistance * maxDistance) { if (count > 0) { if (spawnInterval < FLT_EPSILON) { diff --git a/src/graphics/render/Emitter.hpp b/src/graphics/render/Emitter.hpp index 884ba11e..04355eeb 100644 --- a/src/graphics/render/Emitter.hpp +++ b/src/graphics/render/Emitter.hpp @@ -8,6 +8,7 @@ #include "maths/UVRegion.hpp" #include "maths/util.hpp" +#include "presets/ParticlesPreset.hpp" class Level; class Emitter; @@ -30,12 +31,6 @@ struct Particle { class Texture; -struct ParticleBehaviour { - bool collision = true; - bool lighting = true; - glm::vec3 gravity {0.0f, -16.0f, 0.0f}; -}; - class Emitter { const Level& level; /// @brief Static position or entity @@ -54,12 +49,10 @@ class Emitter { float timer = 0.0f; /// @brief Random velocity magnitude applying to spawned particles. glm::vec3 explosion {8.0f}; - /// @brief Max distance of actually spawning particles. - float maxDistance = 32.0f; util::PseudoRandom random; public: - ParticleBehaviour behaviour; + ParticlesPreset preset; Emitter( const Level& level, diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index 0ad5c1d5..8838f9e8 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -35,12 +35,12 @@ ParticlesRenderer::~ParticlesRenderer() = default; static inline void update_particle( Particle& particle, float delta, const Chunks& chunks ) { - const auto& behave = particle.emitter->behaviour; + const auto& preset = particle.emitter->preset; auto& pos = particle.position; auto& vel = particle.velocity; - vel += delta * behave.gravity; - if (behave.collision && chunks.isObstacleAt(pos + vel * delta)) { + vel += delta * preset.acceleration; + if (preset.collision && chunks.isObstacleAt(pos + vel * delta)) { vel *= 0.0f; } pos += vel * delta; @@ -72,7 +72,7 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { update_particle(particle, delta, chunks); glm::vec4 light(1, 1, 1, 0); - if (particle.emitter->behaviour.lighting) { + if (particle.emitter->preset.lighting) { light = MainBatch::sampleLight( particle.position, chunks, backlight ); diff --git a/src/presets/ParticlesPreset.cpp b/src/presets/ParticlesPreset.cpp new file mode 100644 index 00000000..028b73a4 --- /dev/null +++ b/src/presets/ParticlesPreset.cpp @@ -0,0 +1,20 @@ +#include "ParticlesPreset.hpp" + +#include "data/dv_util.hpp" + +dv::value ParticlesPreset::serialize() const { + auto root = dv::object(); + root["collision"] = collision; + root["lighting"] = lighting; + root["max_distance"] = maxDistance; + root["acceleration"] = dv::to_value(acceleration); + return root; +} + +void ParticlesPreset::deserialize(const dv::value& src) { + src.at("collision").get(collision); + src.at("lighting").get(lighting); + if (src.has("acceleration")) { + dv::get_vec(src["acceleration"], acceleration); + } +} diff --git a/src/presets/ParticlesPreset.hpp b/src/presets/ParticlesPreset.hpp new file mode 100644 index 00000000..07068344 --- /dev/null +++ b/src/presets/ParticlesPreset.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include "interfaces/Serializable.hpp" + +struct ParticlesPreset : public Serializable { + /// @brief Collision detection + bool collision = true; + /// @brief Apply lighting + bool lighting = true; + /// @brief Max distance of actually spawning particles. + float maxDistance = 32.0f; + /// @brief Velocity acceleration + glm::vec3 acceleration {0.0f, -16.0f, 0.0f}; + + dv::value serialize() const override; + void deserialize(const dv::value& src) override; +}; From c8187c5f25592fcb7940106ac72619a6c5bd3448 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 Nov 2024 16:01:07 +0300 Subject: [PATCH 12/32] add particles.emit(...) (WIP) --- res/content/base/scripts/world.lua | 8 ++++ src/frontend/screens/LevelScreen.cpp | 2 +- src/graphics/render/Emitter.cpp | 16 +++---- src/graphics/render/Emitter.hpp | 17 +++---- src/graphics/render/ParticlesRenderer.cpp | 24 ++++------ src/graphics/render/ParticlesRenderer.hpp | 2 + src/graphics/render/WorldRenderer.cpp | 5 +++ src/graphics/render/WorldRenderer.hpp | 5 ++- src/logic/scripting/lua/libs/api_lua.hpp | 1 + src/logic/scripting/lua/libs/libparticles.cpp | 44 +++++++++++++++++++ src/logic/scripting/scripting_hud.cpp | 7 ++- src/logic/scripting/scripting_hud.hpp | 14 +++--- src/presets/ParticlesPreset.cpp | 15 +++++++ src/presets/ParticlesPreset.hpp | 12 +++++ 14 files changed, 125 insertions(+), 47 deletions(-) create mode 100644 res/content/base/scripts/world.lua create mode 100644 src/logic/scripting/lua/libs/libparticles.cpp diff --git a/res/content/base/scripts/world.lua b/res/content/base/scripts/world.lua new file mode 100644 index 00000000..db8be79a --- /dev/null +++ b/res/content/base/scripts/world.lua @@ -0,0 +1,8 @@ +function on_block_broken(id, x, y, z, playerid) + particles.emit({x+0.5, y+0.5, z+0.5}, 100, { + lifetime=1.0, + spawn_interval=0.0001, + explosion={4, 4, 4}, + texture="blocks:"..block.get_textures(id)[1] + }) +end diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index c0d4aa88..b8bb59e6 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -66,7 +66,7 @@ void LevelScreen::initializeContent() { for (auto& entry : content->getPacks()) { initializePack(entry.second.get()); } - scripting::on_frontend_init(hud.get()); + scripting::on_frontend_init(hud.get(), worldRenderer.get()); } void LevelScreen::initializePack(ContentPackRuntime* pack) { diff --git a/src/graphics/render/Emitter.cpp b/src/graphics/render/Emitter.cpp index 7e4d55f7..7c7cb30b 100644 --- a/src/graphics/render/Emitter.cpp +++ b/src/graphics/render/Emitter.cpp @@ -12,18 +12,17 @@ Emitter::Emitter( const Level& level, std::variant origin, - Particle prototype, + ParticlesPreset preset, const Texture* texture, - float spawnInterval, + const UVRegion& region, int count ) : level(level), origin(std::move(origin)), - prototype(std::move(prototype)), + prototype({this, 0, glm::vec3(), preset.velocity, preset.lifetime, region}), texture(texture), - spawnInterval(spawnInterval), count(count), - preset() { + preset(std::move(preset)) { this->prototype.emitter = this; } @@ -36,6 +35,7 @@ void Emitter::update( const glm::vec3& cameraPosition, std::vector& particles ) { + const float spawnInterval = preset.spawnInterval; if (count == 0 || (count == -1 && spawnInterval < FLT_EPSILON)) { return; } @@ -67,7 +67,7 @@ void Emitter::update( particle.emitter = this; particle.random = random.rand32(); particle.position = position; - particle.velocity += glm::ballRand(1.0f) * explosion; + particle.velocity += glm::ballRand(1.0f) * preset.explosion; particles.push_back(std::move(particle)); timer -= spawnInterval; if (count > 0) { @@ -76,10 +76,6 @@ void Emitter::update( } } -void Emitter::setExplosion(const glm::vec3& magnitude) { - explosion = magnitude; -} - bool Emitter::isDead() const { return count == 0; } diff --git a/src/graphics/render/Emitter.hpp b/src/graphics/render/Emitter.hpp index 04355eeb..749c82fd 100644 --- a/src/graphics/render/Emitter.hpp +++ b/src/graphics/render/Emitter.hpp @@ -31,24 +31,22 @@ struct Particle { class Texture; +using EmitterOrigin = std::variant; + class Emitter { const Level& level; /// @brief Static position or entity - std::variant origin; + EmitterOrigin origin; /// @brief Particle prototype Particle prototype; /// @brief Particle const Texture* texture; - /// @brief Particles spawn interval - float spawnInterval; /// @brief Number of particles should be spawned before emitter deactivation. /// -1 is infinite. int count; /// @brief Spawn timer used to determine number of particles /// to spawn on update. May be innacurate. float timer = 0.0f; - /// @brief Random velocity magnitude applying to spawned particles. - glm::vec3 explosion {8.0f}; util::PseudoRandom random; public: @@ -57,9 +55,9 @@ public: Emitter( const Level& level, std::variant origin, - Particle prototype, + ParticlesPreset preset, const Texture* texture, - float spawnInterval, + const UVRegion& region, int count ); @@ -77,10 +75,7 @@ public: const glm::vec3& cameraPosition, std::vector& particles ); - - /// @brief Set initial random velocity magitude - void setExplosion(const glm::vec3& magnitude); - + /// @return true if the emitter has spawned all particles bool isDead() const; }; diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index 8838f9e8..ab099058 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -1,7 +1,6 @@ #include "ParticlesRenderer.hpp" #include "assets/Assets.hpp" -#include "assets/assets_util.hpp" #include "graphics/core/Shader.hpp" #include "graphics/core/Texture.hpp" #include "graphics/render/MainBatch.hpp" @@ -18,17 +17,7 @@ ParticlesRenderer::ParticlesRenderer( ) : batch(std::make_unique(1024)), level(level), - settings(settings) { - auto region = util::get_texture_region(assets, "blocks:grass_side", ""); - emitters.push_back(std::make_unique( - level, - glm::vec3(0, 80, 0), - Particle {nullptr, 0, glm::vec3(), glm::vec3(), 5.0f, region.region}, - region.texture, - 0.002f, - -1 - )); -} + settings(settings) {} ParticlesRenderer::~ParticlesRenderer() = default; @@ -68,21 +57,22 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { auto iter = vec.begin(); while (iter != vec.end()) { auto& particle = *iter; + auto& preset = particle.emitter->preset; update_particle(particle, delta, chunks); glm::vec4 light(1, 1, 1, 0); - if (particle.emitter->preset.lighting) { + if (preset.lighting) { light = MainBatch::sampleLight( particle.position, chunks, backlight ); - light *= 0.8f + (particle.random % 200) * 0.001f; + light *= 0.9f + (particle.random % 100) * 0.001f; } batch->quad( particle.position, right, up, - glm::vec2(0.3f), + preset.size, light, glm::vec3(1.0f), particle.region @@ -128,3 +118,7 @@ void ParticlesRenderer::render(const Camera& camera, float delta) { iter++; } } + +void ParticlesRenderer::add(std::unique_ptr emitter) { + emitters.push_back(std::move(emitter)); +} diff --git a/src/graphics/render/ParticlesRenderer.hpp b/src/graphics/render/ParticlesRenderer.hpp index ca7906da..6986db05 100644 --- a/src/graphics/render/ParticlesRenderer.hpp +++ b/src/graphics/render/ParticlesRenderer.hpp @@ -31,6 +31,8 @@ public: void render(const Camera& camera, float delta); + void add(std::unique_ptr emitter); + static size_t visibleParticles; static size_t aliveEmitters; }; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 730125ad..874a9894 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -44,6 +44,7 @@ #include "ChunksRenderer.hpp" #include "ModelBatch.hpp" #include "Skybox.hpp" +#include "Emitter.hpp" bool WorldRenderer::showChunkBorders = false; bool WorldRenderer::showEntitiesDebug = false; @@ -534,6 +535,10 @@ void WorldRenderer::drawBorders( lineBatch->flush(); } +void WorldRenderer::addEmitter(std::unique_ptr emitter) { + particles->add(std::move(emitter)); +} + void WorldRenderer::clear() { renderer->clear(); } diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index d2ed639a..cf74c6dd 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -8,8 +8,6 @@ #include -#include "Emitter.hpp" - class Level; class Player; class Camera; @@ -27,6 +25,7 @@ class PostProcessing; class DrawContext; class ModelBatch; class Assets; +class Emitter; struct EngineSettings; namespace model { @@ -109,5 +108,7 @@ public: bool pause ); + void addEmitter(std::unique_ptr emitter); + void clear(); }; diff --git a/src/logic/scripting/lua/libs/api_lua.hpp b/src/logic/scripting/lua/libs/api_lua.hpp index f9c8b3f8..a6edc434 100644 --- a/src/logic/scripting/lua/libs/api_lua.hpp +++ b/src/logic/scripting/lua/libs/api_lua.hpp @@ -31,6 +31,7 @@ extern const luaL_Reg itemlib[]; extern const luaL_Reg jsonlib[]; extern const luaL_Reg mat4lib[]; extern const luaL_Reg packlib[]; +extern const luaL_Reg particleslib[]; extern const luaL_Reg playerlib[]; extern const luaL_Reg quatlib[]; // quat.cpp extern const luaL_Reg timelib[]; diff --git a/src/logic/scripting/lua/libs/libparticles.cpp b/src/logic/scripting/lua/libs/libparticles.cpp new file mode 100644 index 00000000..932318ea --- /dev/null +++ b/src/logic/scripting/lua/libs/libparticles.cpp @@ -0,0 +1,44 @@ +#include "api_lua.hpp" + +#include "logic/scripting/scripting_hud.hpp" +#include "graphics/render/WorldRenderer.hpp" +#include "graphics/render/Emitter.hpp" +#include "assets/assets_util.hpp" +#include "engine.hpp" + +using namespace scripting; + +static int l_emit(lua::State* L) { + EmitterOrigin origin; + if (lua::istable(L, 1)) { + origin = lua::tovec3(L, 1); + } else { + origin = static_cast(lua::tointeger(L, 1)); + } + int count = lua::tointeger(L, 2); + auto preset = lua::tovalue(L, 3); + auto extension = lua::tovalue(L, 4); + + ParticlesPreset particlesPreset {}; + particlesPreset.deserialize(preset); + if (extension != nullptr) { + particlesPreset.deserialize(extension); + } + auto& assets = *engine->getAssets(); + auto region = util::get_texture_region(assets, particlesPreset.texture, ""); + auto emitter = std::make_unique( + *level, + std::move(origin), + std::move(particlesPreset), + region.texture, + region.region, + count + ); + renderer->addEmitter(std::move(emitter)); + return 0; +} + +const luaL_Reg particleslib[] = { + {"emit", lua::wrap}, + {NULL, NULL} +}; diff --git a/src/logic/scripting/scripting_hud.cpp b/src/logic/scripting/scripting_hud.cpp index a8856837..f065a692 100644 --- a/src/logic/scripting/scripting_hud.cpp +++ b/src/logic/scripting/scripting_hud.cpp @@ -4,6 +4,7 @@ #include "engine.hpp" #include "files/files.hpp" #include "frontend/hud.hpp" +#include "graphics/render/WorldRenderer.hpp" #include "objects/Player.hpp" #include "lua/libs/api_lua.hpp" #include "lua/lua_engine.hpp" @@ -14,10 +15,14 @@ using namespace scripting; static debug::Logger logger("scripting-hud"); Hud* scripting::hud = nullptr; +WorldRenderer* scripting::renderer = nullptr; -void scripting::on_frontend_init(Hud* hud) { +void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) { scripting::hud = hud; + scripting::renderer = renderer; + lua::openlib(lua::get_main_state(), "hud", hudlib); + lua::openlib(lua::get_main_state(), "particles", particleslib); for (auto& pack : engine->getContentPacks()) { lua::emit_event( diff --git a/src/logic/scripting/scripting_hud.hpp b/src/logic/scripting/scripting_hud.hpp index c2d9b7f5..cea7e8ec 100644 --- a/src/logic/scripting/scripting_hud.hpp +++ b/src/logic/scripting/scripting_hud.hpp @@ -8,20 +8,20 @@ namespace fs = std::filesystem; class Hud; +class WorldRenderer; namespace scripting { extern Hud *hud; + extern WorldRenderer* renderer; - void on_frontend_init(Hud *hud); + void on_frontend_init(Hud* hud, WorldRenderer* renderer); void on_frontend_render(); void on_frontend_close(); - /** - * Load package-specific hud script - * @param env environment id - * @param packid content-pack id - * @param file script file path - */ + /// @brief Load package-specific hud script + /// @param env environment id + /// @param packid content-pack id + /// @param file script file path void load_hud_script( const scriptenv &env, const std::string &packid, const fs::path &file ); diff --git a/src/presets/ParticlesPreset.cpp b/src/presets/ParticlesPreset.cpp index 028b73a4..61833672 100644 --- a/src/presets/ParticlesPreset.cpp +++ b/src/presets/ParticlesPreset.cpp @@ -4,17 +4,32 @@ dv::value ParticlesPreset::serialize() const { auto root = dv::object(); + root["texture"] = texture; root["collision"] = collision; root["lighting"] = lighting; root["max_distance"] = maxDistance; + root["spawn_interval"] = spawnInterval; + root["lifetime"] = lifetime; root["acceleration"] = dv::to_value(acceleration); + root["explosion"] = dv::to_value(explosion); + root["size"] = dv::to_value(size); return root; } void ParticlesPreset::deserialize(const dv::value& src) { + src.at("texture").get(texture); src.at("collision").get(collision); src.at("lighting").get(lighting); + src.at("max_distance").get(maxDistance); + src.at("spawn_interval").get(spawnInterval); + src.at("lifetime").get(lifetime); if (src.has("acceleration")) { dv::get_vec(src["acceleration"], acceleration); } + if (src.has("size")) { + dv::get_vec(src["size"], size); + } + if (src.has("explosion")) { + dv::get_vec(src["explosion"], explosion); + } } diff --git a/src/presets/ParticlesPreset.hpp b/src/presets/ParticlesPreset.hpp index 07068344..4bd2c07d 100644 --- a/src/presets/ParticlesPreset.hpp +++ b/src/presets/ParticlesPreset.hpp @@ -11,8 +11,20 @@ struct ParticlesPreset : public Serializable { bool lighting = true; /// @brief Max distance of actually spawning particles. float maxDistance = 32.0f; + /// @brief Particles spawn interval + float spawnInterval = 0.1f; + /// @brief Particle life time + float lifetime = 5.0f; + /// @brief Initial velocity + glm::vec3 velocity {}; /// @brief Velocity acceleration glm::vec3 acceleration {0.0f, -16.0f, 0.0f}; + /// @brief Random velocity magnitude applying to spawned particles. + glm::vec3 explosion {2.0f}; + /// @brief Particle size + glm::vec3 size {0.1f}; + /// @brief Texture name + std::string texture = ""; dv::value serialize() const override; void deserialize(const dv::value& src) override; From 86b83a53778c94b275f85a07947a028abc8917c9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 Nov 2024 16:49:31 +0300 Subject: [PATCH 13/32] add 'random_sub_uv' particle setting --- res/content/base/scripts/world.lua | 5 +++-- src/graphics/render/Emitter.cpp | 9 +++++++++ src/maths/UVRegion.hpp | 11 +++++++++++ src/presets/ParticlesPreset.cpp | 4 ++++ src/presets/ParticlesPreset.hpp | 4 ++++ 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/res/content/base/scripts/world.lua b/res/content/base/scripts/world.lua index db8be79a..61e1086a 100644 --- a/res/content/base/scripts/world.lua +++ b/res/content/base/scripts/world.lua @@ -1,8 +1,9 @@ function on_block_broken(id, x, y, z, playerid) particles.emit({x+0.5, y+0.5, z+0.5}, 100, { - lifetime=1.0, + lifetime=2.0, spawn_interval=0.0001, explosion={4, 4, 4}, - texture="blocks:"..block.get_textures(id)[1] + texture="blocks:"..block.get_textures(id)[1], + random_sub_uv=0.1 }) end diff --git a/src/graphics/render/Emitter.cpp b/src/graphics/render/Emitter.cpp index 7c7cb30b..c2763574 100644 --- a/src/graphics/render/Emitter.cpp +++ b/src/graphics/render/Emitter.cpp @@ -67,7 +67,16 @@ void Emitter::update( particle.emitter = this; particle.random = random.rand32(); particle.position = position; + particle.lifetime *= 1.0f - preset.lifetimeSpread * random.randFloat(); particle.velocity += glm::ballRand(1.0f) * preset.explosion; + if (preset.randomSubUV < 1.0f) { + particle.region.autoSub( + preset.randomSubUV, + preset.randomSubUV, + random.randFloat(), + random.randFloat() + ); + } particles.push_back(std::move(particle)); timer -= spawnInterval; if (count > 0) { diff --git a/src/maths/UVRegion.hpp b/src/maths/UVRegion.hpp index ec58dfb5..f7bdb2d0 100644 --- a/src/maths/UVRegion.hpp +++ b/src/maths/UVRegion.hpp @@ -22,4 +22,15 @@ struct UVRegion { inline float getHeight() const { return fabs(v2 - v1); } + + void autoSub(float w, float h, float x, float y) { + x *= 1.0f - w; + y *= 1.0f - h; + float uvw = getWidth(); + float uvh = getHeight(); + u1 = u1 + uvw * x; + v1 = v1 + uvh * y; + u2 = u1 + uvw * w; + v2 = v1 + uvh * h; + } }; diff --git a/src/presets/ParticlesPreset.cpp b/src/presets/ParticlesPreset.cpp index 61833672..490e7cc9 100644 --- a/src/presets/ParticlesPreset.cpp +++ b/src/presets/ParticlesPreset.cpp @@ -10,9 +10,11 @@ dv::value ParticlesPreset::serialize() const { root["max_distance"] = maxDistance; root["spawn_interval"] = spawnInterval; root["lifetime"] = lifetime; + root["lifetime_spread"] = lifetimeSpread; root["acceleration"] = dv::to_value(acceleration); root["explosion"] = dv::to_value(explosion); root["size"] = dv::to_value(size); + root["random_sub_uv"] = randomSubUV; return root; } @@ -23,6 +25,8 @@ void ParticlesPreset::deserialize(const dv::value& src) { src.at("max_distance").get(maxDistance); src.at("spawn_interval").get(spawnInterval); src.at("lifetime").get(lifetime); + src.at("lifetime_spread").get(lifetimeSpread); + src.at("random_sub_uv").get(randomSubUV); if (src.has("acceleration")) { dv::get_vec(src["acceleration"], acceleration); } diff --git a/src/presets/ParticlesPreset.hpp b/src/presets/ParticlesPreset.hpp index 4bd2c07d..48658f8c 100644 --- a/src/presets/ParticlesPreset.hpp +++ b/src/presets/ParticlesPreset.hpp @@ -15,6 +15,8 @@ struct ParticlesPreset : public Serializable { float spawnInterval = 0.1f; /// @brief Particle life time float lifetime = 5.0f; + /// @brief Life time spread divided by lifetime + float lifetimeSpread = 0.2f; /// @brief Initial velocity glm::vec3 velocity {}; /// @brief Velocity acceleration @@ -25,6 +27,8 @@ struct ParticlesPreset : public Serializable { glm::vec3 size {0.1f}; /// @brief Texture name std::string texture = ""; + /// @brief Size of random sub-uv region + float randomSubUV = 1.0f; dv::value serialize() const override; void deserialize(const dv::value& src) override; From 39893ee940d36acc18beaf0ea35cc6e8eba1e4ff Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 Nov 2024 17:14:30 +0300 Subject: [PATCH 14/32] add 'spawn_spread' and 'spawn_shape' --- res/content/base/scripts/world.lua | 5 ++++- src/graphics/render/Emitter.cpp | 19 ++++++++++++++++++- src/presets/ParticlesPreset.cpp | 27 +++++++++++++++++++++++++++ src/presets/ParticlesPreset.hpp | 19 +++++++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/res/content/base/scripts/world.lua b/res/content/base/scripts/world.lua index 61e1086a..d8e76967 100644 --- a/res/content/base/scripts/world.lua +++ b/res/content/base/scripts/world.lua @@ -4,6 +4,9 @@ function on_block_broken(id, x, y, z, playerid) spawn_interval=0.0001, explosion={4, 4, 4}, texture="blocks:"..block.get_textures(id)[1], - random_sub_uv=0.1 + random_sub_uv=0.1, + size={0.1, 0.1, 0.1}, + spawn_shape="box", + spawn_spread={0.4, 0.4, 0.4} }) end diff --git a/src/graphics/render/Emitter.cpp b/src/graphics/render/Emitter.cpp index c2763574..96bcb96d 100644 --- a/src/graphics/render/Emitter.cpp +++ b/src/graphics/render/Emitter.cpp @@ -30,6 +30,19 @@ const Texture* Emitter::getTexture() const { return texture; } +static inline glm::vec3 generate_coord(ParticleSpawnShape shape) { + switch (shape) { + case ParticleSpawnShape::BALL: + return glm::ballRand(1.0f); + case ParticleSpawnShape::SPHERE: + return glm::sphericalRand(1.0f); + case ParticleSpawnShape::BOX: + return glm::linearRand(glm::vec3(-1.0f), glm::vec3(1.0f)); + default: + return {}; + } +} + void Emitter::update( float delta, const glm::vec3& cameraPosition, @@ -66,7 +79,11 @@ void Emitter::update( Particle particle = prototype; particle.emitter = this; particle.random = random.rand32(); - particle.position = position; + + glm::vec3 spawnOffset = generate_coord(preset.spawnShape); + spawnOffset *= preset.spawnSpread; + + particle.position = position + spawnOffset; particle.lifetime *= 1.0f - preset.lifetimeSpread * random.randFloat(); particle.velocity += glm::ballRand(1.0f) * preset.explosion; if (preset.randomSubUV < 1.0f) { diff --git a/src/presets/ParticlesPreset.cpp b/src/presets/ParticlesPreset.cpp index 490e7cc9..7d9f86ab 100644 --- a/src/presets/ParticlesPreset.cpp +++ b/src/presets/ParticlesPreset.cpp @@ -2,6 +2,25 @@ #include "data/dv_util.hpp" +std::string to_string(ParticleSpawnShape shape) { + static std::string names[] = { + "ball", + "sphere", + "box" + }; + return names[static_cast(shape)]; +} + +ParticleSpawnShape ParticleSpawnShape_from(std::string_view s) { + if (s == "ball") { + return ParticleSpawnShape::BALL; + } else if (s == "sphere") { + return ParticleSpawnShape::SPHERE; + } else { + return ParticleSpawnShape::BOX; + } +} + dv::value ParticlesPreset::serialize() const { auto root = dv::object(); root["texture"] = texture; @@ -14,6 +33,8 @@ dv::value ParticlesPreset::serialize() const { root["acceleration"] = dv::to_value(acceleration); root["explosion"] = dv::to_value(explosion); root["size"] = dv::to_value(size); + root["spawn_spread"] = dv::to_value(size); + root["spawn_shape"] = to_string(spawnShape); root["random_sub_uv"] = randomSubUV; return root; } @@ -33,7 +54,13 @@ void ParticlesPreset::deserialize(const dv::value& src) { if (src.has("size")) { dv::get_vec(src["size"], size); } + if (src.has("spawn_spread")) { + dv::get_vec(src["spawn_spread"], spawnSpread); + } if (src.has("explosion")) { dv::get_vec(src["explosion"], explosion); } + if (src.has("spawn_shape")) { + spawnShape = ParticleSpawnShape_from(src["spawn_shape"].asString()); + } } diff --git a/src/presets/ParticlesPreset.hpp b/src/presets/ParticlesPreset.hpp index 48658f8c..38fc1214 100644 --- a/src/presets/ParticlesPreset.hpp +++ b/src/presets/ParticlesPreset.hpp @@ -4,6 +4,21 @@ #include "interfaces/Serializable.hpp" +enum ParticleSpawnShape { + /// @brief Coordinates are regulary distributed within + /// the volume of a ball. + BALL = 0, + /// @brief Coordinates are regulary distributed on + /// a sphere. + SPHERE, + /// @brief Coordinates are uniform distributed within + /// the volume of a box. + BOX +}; + +std::string to_string(ParticleSpawnShape shape); +ParticleSpawnShape ParticleSpawnShape_from(std::string_view s); + struct ParticlesPreset : public Serializable { /// @brief Collision detection bool collision = true; @@ -25,6 +40,10 @@ struct ParticlesPreset : public Serializable { glm::vec3 explosion {2.0f}; /// @brief Particle size glm::vec3 size {0.1f}; + /// @brief Spawn spread shape + ParticleSpawnShape spawnShape; + /// @brief Spawn spread + glm::vec3 spawnSpread {}; /// @brief Texture name std::string texture = ""; /// @brief Size of random sub-uv region From b9b122dc83f77b67c9a6214adc60935c3c08d75b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 Nov 2024 19:18:26 +0300 Subject: [PATCH 15/32] add 'frames' particles setting --- src/graphics/render/Emitter.cpp | 7 +++++++ src/graphics/render/Emitter.hpp | 5 ++++- src/graphics/render/ParticlesRenderer.cpp | 19 +++++++++++++++++++ src/graphics/render/ParticlesRenderer.hpp | 1 + src/presets/ParticlesPreset.cpp | 15 +++++++++++++++ src/presets/ParticlesPreset.hpp | 2 ++ 6 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/graphics/render/Emitter.cpp b/src/graphics/render/Emitter.cpp index 96bcb96d..046d6486 100644 --- a/src/graphics/render/Emitter.cpp +++ b/src/graphics/render/Emitter.cpp @@ -58,6 +58,9 @@ void Emitter::update( } else if (auto entityId = std::get_if(&origin)) { if (auto entity = level.entities->get(*entityId)) { position = entity->getTransform().pos; + } else { + stop(); + return; } } const float maxDistance = preset.maxDistance; @@ -102,6 +105,10 @@ void Emitter::update( } } +void Emitter::stop() { + count = 0; +} + bool Emitter::isDead() const { return count == 0; } diff --git a/src/graphics/render/Emitter.hpp b/src/graphics/render/Emitter.hpp index 749c82fd..24867edd 100644 --- a/src/graphics/render/Emitter.hpp +++ b/src/graphics/render/Emitter.hpp @@ -75,7 +75,10 @@ public: const glm::vec3& cameraPosition, std::vector& particles ); - + + /// @brief Set remaining particles count to 0 + void stop(); + /// @return true if the emitter has spawned all particles bool isDead() const; }; diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index ab099058..a15e75a8 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -1,6 +1,7 @@ #include "ParticlesRenderer.hpp" #include "assets/Assets.hpp" +#include "assets/assets_util.hpp" #include "graphics/core/Shader.hpp" #include "graphics/core/Texture.hpp" #include "graphics/render/MainBatch.hpp" @@ -17,6 +18,7 @@ ParticlesRenderer::ParticlesRenderer( ) : batch(std::make_unique(1024)), level(level), + assets(assets), settings(settings) {} ParticlesRenderer::~ParticlesRenderer() = default; @@ -59,6 +61,23 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { auto& particle = *iter; auto& preset = particle.emitter->preset; + if (!preset.frames.empty()) { + float time = preset.lifetime - particle.lifetime; + int framesCount = preset.frames.size(); + int frameid = time / preset.lifetime * framesCount; + int frameid2 = glm::min( + (time + delta) / preset.lifetime * framesCount, + framesCount - 1.0f + ); + if (frameid2 != frameid) { + auto tregion = util::get_texture_region( + assets, preset.frames.at(frameid2), "" + ); + if (tregion.texture == texture) { + particle.region = tregion.region; + } + } + } update_particle(particle, delta, chunks); glm::vec4 light(1, 1, 1, 0); diff --git a/src/graphics/render/ParticlesRenderer.hpp b/src/graphics/render/ParticlesRenderer.hpp index 6986db05..27877959 100644 --- a/src/graphics/render/ParticlesRenderer.hpp +++ b/src/graphics/render/ParticlesRenderer.hpp @@ -15,6 +15,7 @@ struct GraphicsSettings; class ParticlesRenderer { const Level& level; + const Assets& assets; const GraphicsSettings* settings; std::unordered_map> particles; std::vector> emitters; diff --git a/src/presets/ParticlesPreset.cpp b/src/presets/ParticlesPreset.cpp index 7d9f86ab..f832507d 100644 --- a/src/presets/ParticlesPreset.cpp +++ b/src/presets/ParticlesPreset.cpp @@ -36,6 +36,12 @@ dv::value ParticlesPreset::serialize() const { root["spawn_spread"] = dv::to_value(size); root["spawn_shape"] = to_string(spawnShape); root["random_sub_uv"] = randomSubUV; + if (!frames.empty()) { + auto& arr = root.list("animation"); + for (const auto& frame : frames) { + arr.add(frame); + } + } return root; } @@ -63,4 +69,13 @@ void ParticlesPreset::deserialize(const dv::value& src) { if (src.has("spawn_shape")) { spawnShape = ParticleSpawnShape_from(src["spawn_shape"].asString()); } + if (src.has("frames")) { + for (const auto& frame : src["frames"]) { + frames.push_back(frame.asString()); + } + if (!frames.empty()) { + texture = frames.at(0); + randomSubUV = 1.0f; + } + } } diff --git a/src/presets/ParticlesPreset.hpp b/src/presets/ParticlesPreset.hpp index 38fc1214..fcaddb3f 100644 --- a/src/presets/ParticlesPreset.hpp +++ b/src/presets/ParticlesPreset.hpp @@ -48,6 +48,8 @@ struct ParticlesPreset : public Serializable { std::string texture = ""; /// @brief Size of random sub-uv region float randomSubUV = 1.0f; + /// @brief Animation frames + std::vector frames {}; dv::value serialize() const override; void deserialize(const dv::value& src) override; From f1dc983766fac81993afb5c409525ad2d484528f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 Nov 2024 19:51:59 +0300 Subject: [PATCH 16/32] add standard 'particles' atlas --- res/preload.json | 3 ++- src/presets/ParticlesPreset.cpp | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/res/preload.json b/res/preload.json index 72f96c6b..8971103f 100644 --- a/res/preload.json +++ b/res/preload.json @@ -27,6 +27,7 @@ ], "atlases": [ "blocks", - "items" + "items", + "particles" ] } diff --git a/src/presets/ParticlesPreset.cpp b/src/presets/ParticlesPreset.cpp index f832507d..1414083a 100644 --- a/src/presets/ParticlesPreset.cpp +++ b/src/presets/ParticlesPreset.cpp @@ -23,25 +23,27 @@ ParticleSpawnShape ParticleSpawnShape_from(std::string_view s) { dv::value ParticlesPreset::serialize() const { auto root = dv::object(); - root["texture"] = texture; + if (frames.empty()) { + root["texture"] = texture; + } else { + auto& arr = root.list("animation"); + for (const auto& frame : frames) { + arr.add(frame); + } + } root["collision"] = collision; root["lighting"] = lighting; root["max_distance"] = maxDistance; root["spawn_interval"] = spawnInterval; root["lifetime"] = lifetime; root["lifetime_spread"] = lifetimeSpread; + root["velocity"] = dv::to_value(velocity); root["acceleration"] = dv::to_value(acceleration); root["explosion"] = dv::to_value(explosion); root["size"] = dv::to_value(size); root["spawn_spread"] = dv::to_value(size); root["spawn_shape"] = to_string(spawnShape); root["random_sub_uv"] = randomSubUV; - if (!frames.empty()) { - auto& arr = root.list("animation"); - for (const auto& frame : frames) { - arr.add(frame); - } - } return root; } @@ -54,6 +56,9 @@ void ParticlesPreset::deserialize(const dv::value& src) { src.at("lifetime").get(lifetime); src.at("lifetime_spread").get(lifetimeSpread); src.at("random_sub_uv").get(randomSubUV); + if (src.has("velocity")) { + dv::get_vec(src["velocity"], velocity); + } if (src.has("acceleration")) { dv::get_vec(src["acceleration"], acceleration); } From 8bfc7983dadc5389512266a45adff8191b2d89cd Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 4 Nov 2024 01:31:41 +0300 Subject: [PATCH 17/32] add test block particles --- res/content/base/scripts/torch.lua | 18 ++++++++++++++++++ .../base/textures/particles/fire_0.png | Bin 0 -> 5195 bytes .../base/textures/particles/smoke_0.png | Bin 0 -> 5120 bytes .../base/textures/particles/smoke_1.png | Bin 0 -> 5121 bytes 4 files changed, 18 insertions(+) create mode 100644 res/content/base/scripts/torch.lua create mode 100644 res/content/base/textures/particles/fire_0.png create mode 100644 res/content/base/textures/particles/smoke_0.png create mode 100644 res/content/base/textures/particles/smoke_1.png diff --git a/res/content/base/scripts/torch.lua b/res/content/base/scripts/torch.lua new file mode 100644 index 00000000..7a1d2848 --- /dev/null +++ b/res/content/base/scripts/torch.lua @@ -0,0 +1,18 @@ +function on_placed(x, y, z) + particles.emit({x + 0.5, y + 0.4, z + 0.5}, -1, { + lifetime=2.0, + spawn_interval=0.3, + acceleration={0, 0, 0}, + velocity={0, 0.3, 0}, + explosion={0, 0, 0}, + size={0.2, 0.2, 0.2}, + spawn_shape="ball", + spawn_spread={0.05, 0.05, 0.05}, + lighting=false, + frames={ + "particles:fire_0", + "particles:smoke_0", + "particles:smoke_1" + } + }) +end diff --git a/res/content/base/textures/particles/fire_0.png b/res/content/base/textures/particles/fire_0.png new file mode 100644 index 0000000000000000000000000000000000000000..b17942d31111b80969349708fdd9706e7cafdb72 GIT binary patch literal 5195 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 literal 0 HcmV?d00001 diff --git a/res/content/base/textures/particles/smoke_1.png b/res/content/base/textures/particles/smoke_1.png new file mode 100644 index 0000000000000000000000000000000000000000..469971d12a9667e8e689b8bea86cc22314f71588 GIT binary patch literal 5121 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 literal 0 HcmV?d00001 From 0cd5136fdb53c47dcc246bc09a4bdeb99c132d70 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 4 Nov 2024 14:41:52 +0300 Subject: [PATCH 18/32] feat: emitters random access & add ParticlesRenderer.gc() --- src/graphics/render/ParticlesRenderer.cpp | 36 +++++++++++++++++++++-- src/graphics/render/ParticlesRenderer.hpp | 17 +++++++++-- src/typedefs.hpp | 1 + 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index a15e75a8..d377773e 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -1,5 +1,7 @@ #include "ParticlesRenderer.hpp" +#include + #include "assets/Assets.hpp" #include "assets/assets_util.hpp" #include "graphics/core/Shader.hpp" @@ -119,7 +121,7 @@ void ParticlesRenderer::render(const Camera& camera, float delta) { auto iter = emitters.begin(); while (iter != emitters.end()) { - auto& emitter = **iter; + auto& emitter = *iter->second; auto texture = emitter.getTexture(); const auto& found = particles.find(texture); std::vector* vec; @@ -138,6 +140,34 @@ void ParticlesRenderer::render(const Camera& camera, float delta) { } } -void ParticlesRenderer::add(std::unique_ptr emitter) { - emitters.push_back(std::move(emitter)); +void ParticlesRenderer::gc() { + std::set usedEmitters; + for (const auto& [_, vec] : particles) { + for (const auto& particle : vec) { + usedEmitters.insert(particle.emitter); + } + } + auto iter = emitters.begin(); + while (iter != emitters.end()) { + auto emitter = iter->second.get(); + if (usedEmitters.find(emitter) == usedEmitters.end()) { + iter = emitters.erase(iter); + } else { + iter++; + } + } +} + +Emitter* ParticlesRenderer::getEmitter(u64id_t id) const { + const auto& found = emitters.find(id); + if (found == emitters.end()) { + return nullptr; + } + return found->second.get(); +} + +u64id_t ParticlesRenderer::add(std::unique_ptr emitter) { + u64id_t uid = nextEmitter++; + emitters[uid] = std::move(emitter); + return uid; } diff --git a/src/graphics/render/ParticlesRenderer.hpp b/src/graphics/render/ParticlesRenderer.hpp index 27877959..46509d96 100644 --- a/src/graphics/render/ParticlesRenderer.hpp +++ b/src/graphics/render/ParticlesRenderer.hpp @@ -5,6 +5,7 @@ #include #include "Emitter.hpp" +#include "typedefs.hpp" class Texture; class Assets; @@ -18,9 +19,11 @@ class ParticlesRenderer { const Assets& assets; const GraphicsSettings* settings; std::unordered_map> particles; - std::vector> emitters; std::unique_ptr batch; + std::unordered_map> emitters; + u64id_t nextEmitter = 1; + void renderParticles(const Camera& camera, float delta); public: ParticlesRenderer( @@ -32,7 +35,17 @@ public: void render(const Camera& camera, float delta); - void add(std::unique_ptr emitter); + u64id_t add(std::unique_ptr emitter); + + /// @brief Perform garbage collection (remove extra dead emitters). + /// @note Emitters are deleting without GC when there's no particles with same + /// texture left. + /// @note Currently unused + void gc(); + + /// @brief Get emitter by UID + /// @return Emitter or nullptr + Emitter* getEmitter(u64id_t id) const; static size_t visibleParticles; static size_t aliveEmitters; diff --git a/src/typedefs.hpp b/src/typedefs.hpp index 3925d277..b3266aea 100644 --- a/src/typedefs.hpp +++ b/src/typedefs.hpp @@ -12,6 +12,7 @@ using integer_t = int64_t; using number_t = double; using uint = unsigned int; +using u64id_t = uint64_t; /// @brief use for bytes arrays using ubyte = uint8_t; From 3ecb7b447f2ca3bee2bbb4ce719f180f3b2a4966 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 4 Nov 2024 14:55:35 +0300 Subject: [PATCH 19/32] add particles.stop(...) --- res/content/base/scripts/world.lua | 4 ++-- src/graphics/render/WorldRenderer.cpp | 4 ---- src/graphics/render/WorldRenderer.hpp | 7 ++++--- src/logic/scripting/lua/libs/libparticles.cpp | 11 ++++++++++- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/res/content/base/scripts/world.lua b/res/content/base/scripts/world.lua index d8e76967..5719c4fb 100644 --- a/res/content/base/scripts/world.lua +++ b/res/content/base/scripts/world.lua @@ -1,6 +1,6 @@ function on_block_broken(id, x, y, z, playerid) - particles.emit({x+0.5, y+0.5, z+0.5}, 100, { - lifetime=2.0, + particles.emit({x+0.5, y+0.5, z+0.5}, 64, { + lifetime=1.0, spawn_interval=0.0001, explosion={4, 4, 4}, texture="blocks:"..block.get_textures(id)[1], diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 874a9894..89b5474b 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -535,10 +535,6 @@ void WorldRenderer::drawBorders( lineBatch->flush(); } -void WorldRenderer::addEmitter(std::unique_ptr emitter) { - particles->add(std::move(emitter)); -} - void WorldRenderer::clear() { renderer->clear(); } diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index cf74c6dd..22e864c5 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -8,6 +8,8 @@ #include +#include "typedefs.hpp" + class Level; class Player; class Camera; @@ -42,7 +44,6 @@ class WorldRenderer { std::unique_ptr skybox; std::unique_ptr batch3d; std::unique_ptr modelBatch; - std::unique_ptr particles; float timer = 0.0f; @@ -80,6 +81,8 @@ class WorldRenderer { float fogFactor ); public: + std::unique_ptr particles; + static bool showChunkBorders; static bool showEntitiesDebug; @@ -108,7 +111,5 @@ public: bool pause ); - void addEmitter(std::unique_ptr emitter); - void clear(); }; diff --git a/src/logic/scripting/lua/libs/libparticles.cpp b/src/logic/scripting/lua/libs/libparticles.cpp index 932318ea..9b488d7a 100644 --- a/src/logic/scripting/lua/libs/libparticles.cpp +++ b/src/logic/scripting/lua/libs/libparticles.cpp @@ -2,6 +2,7 @@ #include "logic/scripting/scripting_hud.hpp" #include "graphics/render/WorldRenderer.hpp" +#include "graphics/render/ParticlesRenderer.hpp" #include "graphics/render/Emitter.hpp" #include "assets/assets_util.hpp" #include "engine.hpp" @@ -34,11 +35,19 @@ static int l_emit(lua::State* L) { region.region, count ); - renderer->addEmitter(std::move(emitter)); + return lua::pushinteger(L, renderer->particles->add(std::move(emitter))); +} + +static int l_stop(lua::State* L) { + u64id_t id = lua::touinteger(L, 1); + if (auto emitter = renderer->particles->getEmitter(id)) { + emitter->stop(); + } return 0; } const luaL_Reg particleslib[] = { {"emit", lua::wrap}, + {"stop", lua::wrap}, {NULL, NULL} }; From 1717b7404260a0d1142ee82ae767508ff2a2f734 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 4 Nov 2024 15:25:23 +0300 Subject: [PATCH 20/32] fix 'time.daytime reset' --- res/scripts/stdcmd.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/scripts/stdcmd.lua b/res/scripts/stdcmd.lua index 437c2460..6dec6ca4 100644 --- a/res/scripts/stdcmd.lua +++ b/res/scripts/stdcmd.lua @@ -120,7 +120,7 @@ console.add_command( world.set_day_time_speed(0) return "Daily cycle has stopped" else - world.set_day_time_speed(1.0 / 60.0 / 24.0) + world.set_day_time_speed(1.0) return "Daily cycle has started" end end From 22cb3acd11e02ce87f8936fc8cd053e20178dc63 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 4 Nov 2024 15:25:55 +0300 Subject: [PATCH 21/32] optimize Skybox --- src/graphics/render/Skybox.cpp | 47 ++++++++++++++++++++++------------ src/graphics/render/Skybox.hpp | 7 +++++ 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/graphics/render/Skybox.cpp b/src/graphics/render/Skybox.cpp index 22927b3c..b1975187 100644 --- a/src/graphics/render/Skybox.cpp +++ b/src/graphics/render/Skybox.cpp @@ -138,6 +138,7 @@ void Skybox::draw( } void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality) { + frameid++; float dayTime = t; DrawContext ctx = pctx.sub(); ctx.setDepthMask(false); @@ -152,7 +153,31 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality) glActiveTexture(GL_TEXTURE1); cubemap->bind(); shader->use(); + t *= M_PI*2.0f; + + lightDir = glm::normalize(glm::vec3(sin(t), -cos(t), 0.0f)); + shader->uniform1i("u_quality", quality); + shader->uniform1f("u_mie", mie); + 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) { + for (uint face = 0; face < 6; face++) { + refreshFace(face, cubemap); + } + } else { + uint face = frameid % 6; + refreshFace(face, cubemap); + } + prevMie = mie; + prevT = t; + + cubemap->unbind(); + glActiveTexture(GL_TEXTURE0); +} + +void Skybox::refreshFace(uint face, Cubemap* cubemap) { const glm::vec3 xaxs[] = { {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, 1.0f}, @@ -181,23 +206,11 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality) {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, 1.0f}, }; - t *= M_PI*2.0f; - - lightDir = glm::normalize(glm::vec3(sin(t), -cos(t), 0.0f)); - shader->uniform1i("u_quality", quality); - shader->uniform1f("u_mie", mie); - shader->uniform1f("u_fog", mie - 1.0f); - shader->uniform3f("u_lightDir", lightDir); - shader->uniform1f("u_dayTime", dayTime); - for (uint face = 0; face < 6; face++) { - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, cubemap->getId(), 0); - shader->uniform3f("u_xaxis", xaxs[face]); - shader->uniform3f("u_yaxis", yaxs[face]); - shader->uniform3f("u_zaxis", zaxs[face]); - mesh->draw(); - } - cubemap->unbind(); - glActiveTexture(GL_TEXTURE0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, cubemap->getId(), 0); + shader->uniform3f("u_xaxis", xaxs[face]); + shader->uniform3f("u_yaxis", yaxs[face]); + shader->uniform3f("u_zaxis", zaxs[face]); + mesh->draw(); } void Skybox::bind() const { diff --git a/src/graphics/render/Skybox.hpp b/src/graphics/render/Skybox.hpp index 2781a95a..902d8fe2 100644 --- a/src/graphics/render/Skybox.hpp +++ b/src/graphics/render/Skybox.hpp @@ -12,6 +12,8 @@ class Shader; class Assets; class Camera; class Batch3D; +class Shader; +class Cubemap; class Framebuffer; class DrawContext; @@ -33,11 +35,16 @@ class Skybox { std::unique_ptr mesh; std::unique_ptr batch3d; std::vector sprites; + int frameid = 0; + + float prevMie = -1.0f; + float prevT = -1.0f; void drawStars(float angle, float opacity); void drawBackground( const Camera& camera, const Assets& assets, int width, int height ); + void refreshFace(uint face, Cubemap* cubemap); public: Skybox(uint size, Shader* shader); ~Skybox(); From 5a431ed8985e34e898ae66fe02ccdc7db2709d7e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 4 Nov 2024 18:02:42 +0300 Subject: [PATCH 22/32] add custom-models in hand support --- src/graphics/core/Model.cpp | 38 +++++++++++++++- src/graphics/core/Model.hpp | 26 +++++++++-- src/graphics/render/MainBatch.cpp | 53 ++++++++++++++++++++++ src/graphics/render/MainBatch.hpp | 8 ++++ src/graphics/render/ModelsGenerator.cpp | 58 +++++++++++++++++++++++++ src/voxels/Block.hpp | 4 +- 6 files changed, 180 insertions(+), 7 deletions(-) diff --git a/src/graphics/core/Model.cpp b/src/graphics/core/Model.cpp index 3da5d897..3595a8e2 100644 --- a/src/graphics/core/Model.cpp +++ b/src/graphics/core/Model.cpp @@ -8,7 +8,12 @@ inline constexpr glm::vec3 X(1, 0, 0); inline constexpr glm::vec3 Y(0, 1, 0); inline constexpr glm::vec3 Z(0, 0, 1); -void Mesh::addPlane(glm::vec3 pos, glm::vec3 right, glm::vec3 up, glm::vec3 norm) { +void Mesh::addPlane( + const glm::vec3& pos, + const glm::vec3& right, + const glm::vec3& up, + const glm::vec3& norm +) { vertices.push_back({pos-right-up, {0,0}, norm}); vertices.push_back({pos+right-up, {1,0}, norm}); vertices.push_back({pos+right+up, {1,1}, norm}); @@ -18,7 +23,23 @@ void Mesh::addPlane(glm::vec3 pos, glm::vec3 right, glm::vec3 up, glm::vec3 norm vertices.push_back({pos-right+up, {0,1}, norm}); } -void Mesh::addBox(glm::vec3 pos, glm::vec3 size) { +void Mesh::addPlane( + const glm::vec3& pos, + const glm::vec3& right, + const glm::vec3& up, + const glm::vec3& norm, + const UVRegion& uv +) { + vertices.push_back({pos-right-up, {uv.u1, uv.v1}, norm}); + vertices.push_back({pos+right-up, {uv.u2, uv.v1}, norm}); + vertices.push_back({pos+right+up, {uv.u2, uv.v2}, norm}); + + vertices.push_back({pos-right-up, {uv.u1, uv.v1}, norm}); + vertices.push_back({pos+right+up, {uv.u2, uv.v2}, norm}); + vertices.push_back({pos-right+up, {uv.u1, uv.u2}, norm}); +} + +void Mesh::addBox(const glm::vec3& pos, const glm::vec3& size) { addPlane(pos+Z*size, X*size, Y*size, Z); addPlane(pos-Z*size, -X*size, Y*size, -Z); @@ -29,6 +50,19 @@ void Mesh::addBox(glm::vec3 pos, glm::vec3 size) { addPlane(pos-X*size, Z*size, Y*size, -X); } +void Mesh::addBox( + const glm::vec3& pos, const glm::vec3& size, const UVRegion (&uvs)[6] +) { + addPlane(pos+Z*size, X*size, Y*size, Z, uvs[0]); + addPlane(pos-Z*size, -X*size, Y*size, -Z, uvs[1]); + + addPlane(pos+Y*size, X*size, -Z*size, Y, uvs[2]); + addPlane(pos-Y*size, X*size, Z*size, -Y, uvs[3]); + + addPlane(pos+X*size, -Z*size, Y*size, X, uvs[4]); + addPlane(pos-X*size, Z*size, Y*size, -X, uvs[5]); +} + void Mesh::scale(const glm::vec3& size) { for (auto& vertex : vertices) { vertex.coord *= size; diff --git a/src/graphics/core/Model.hpp b/src/graphics/core/Model.hpp index b92fb030..da295cbb 100644 --- a/src/graphics/core/Model.hpp +++ b/src/graphics/core/Model.hpp @@ -4,6 +4,8 @@ #include #include +#include "maths/UVRegion.hpp" + namespace model { struct Vertex { glm::vec3 coord; @@ -14,9 +16,27 @@ namespace model { struct Mesh { std::string texture; std::vector vertices; - - void addPlane(glm::vec3 pos, glm::vec3 right, glm::vec3 up, glm::vec3 norm); - void addBox(glm::vec3 pos, glm::vec3 size); + bool lighting = true; + + void addPlane( + const glm::vec3& pos, + const glm::vec3& right, + const glm::vec3& up, + const glm::vec3& norm + ); + void addPlane( + const glm::vec3& pos, + const glm::vec3& right, + const glm::vec3& up, + const glm::vec3& norm, + const UVRegion& region + ); + void addBox(const glm::vec3& pos, const glm::vec3& size); + void addBox( + const glm::vec3& pos, + const glm::vec3& size, + const UVRegion (&texfaces)[6] + ); void scale(const glm::vec3& size); }; diff --git a/src/graphics/render/MainBatch.cpp b/src/graphics/render/MainBatch.cpp index c07a798a..1cbb6c42 100644 --- a/src/graphics/render/MainBatch.cpp +++ b/src/graphics/render/MainBatch.cpp @@ -80,3 +80,56 @@ glm::vec4 MainBatch::sampleLight( glm::max(Lightmap::extract(light, 3), minIntensity) / 15.0f ); } + +inline glm::vec4 do_tint(float value) { + return glm::vec4(value, value, value, 1.0f); +} + +void MainBatch::cube( + const glm::vec3& coord, + const glm::vec3& size, + const UVRegion(&texfaces)[6], + const glm::vec4& tint, + bool shading +) { + const glm::vec3 X(1.0f, 0.0f, 0.0f); + const glm::vec3 Y(0.0f, 1.0f, 0.0f); + const glm::vec3 Z(0.0f, 0.0f, 1.0f); + + quad( + coord + glm::vec3(0.0f, 0.0f, 0.0f), + X, Y, glm::vec2(size.x, size.y), + (shading ? do_tint(0.8) * tint : tint), + glm::vec3(1.0f), texfaces[5] + ); + quad( + coord + glm::vec3(size.x, 0.0f, -size.z), + -X, Y, glm::vec2(size.x, size.y), + (shading ? do_tint(0.8) * tint : tint), + glm::vec3(1.0f), texfaces[4] + ); + quad( + coord + glm::vec3(0.0f, size.y, 0.0f), + X, -Z, glm::vec2(size.x, size.z), + (shading ? do_tint(1.0f) * tint : tint), + glm::vec3(1.0f), texfaces[3] + ); + quad( + coord + glm::vec3(0.0f, 0.0f, -size.z), + X, Z, glm::vec2(size.x, size.z), + (shading ? do_tint(0.7f) * tint : tint), + glm::vec3(1.0f), texfaces[2] + ); + quad( + coord + glm::vec3(0.0f, 0.0f, -size.z), + Z, Y, glm::vec2(size.z, size.y), + (shading ? do_tint(0.9f) * tint : tint), + glm::vec3(1.0f), texfaces[0] + ); + quad( + coord + glm::vec3(size.x, 0.0f, 0.0f), + -Z, Y, glm::vec2(size.z, size.y), + (shading ? do_tint(0.9f) * tint : tint), + glm::vec3(1.0f), texfaces[1] + ); +} diff --git a/src/graphics/render/MainBatch.hpp b/src/graphics/render/MainBatch.hpp index bb25e83b..36bf7a8e 100644 --- a/src/graphics/render/MainBatch.hpp +++ b/src/graphics/render/MainBatch.hpp @@ -118,4 +118,12 @@ public: tint ); } + + void cube( + const glm::vec3& coord, + const glm::vec3& size, + const UVRegion(&texfaces)[6], + const glm::vec4& tint, + bool shading + ); }; diff --git a/src/graphics/render/ModelsGenerator.cpp b/src/graphics/render/ModelsGenerator.cpp index edec061c..07bf7b0d 100644 --- a/src/graphics/render/ModelsGenerator.cpp +++ b/src/graphics/render/ModelsGenerator.cpp @@ -1,6 +1,7 @@ #include "ModelsGenerator.hpp" #include "assets/Assets.hpp" +#include "assets/assets_util.hpp" #include "items/ItemDef.hpp" #include "voxels/Block.hpp" #include "content/Content.hpp" @@ -40,6 +41,13 @@ static model::Model create_flat_model( return model; } +static inline UVRegion get_region_for( + const std::string& texture, const Assets& assets +) { + auto texreg = util::get_texture_region(assets, "blocks:" + texture, ""); + return texreg.region; +} + model::Model ModelsGenerator::generate( const ItemDef& def, const Content& content, const Assets& assets ) { @@ -50,6 +58,56 @@ model::Model ModelsGenerator::generate( return create_flat_model( "blocks:" + blockDef.textureFaces.at(0), assets ); + } else if (blockDef.model == BlockModel::custom) { + model = model::Model(); + for (size_t i = 0; i < blockDef.modelBoxes.size(); i++) { + auto& mesh = + model.addMesh("blocks:" + blockDef.modelTextures[i * 6]); + mesh.lighting = !def.rt.emissive; + const UVRegion (&boxtexfaces)[6] = { + get_region_for(blockDef.modelTextures[i * 6], assets), + get_region_for(blockDef.modelTextures[i * 6 + 1], assets), + get_region_for(blockDef.modelTextures[i * 6 + 2], assets), + get_region_for(blockDef.modelTextures[i * 6 + 3], assets), + get_region_for(blockDef.modelTextures[i * 6 + 4], assets), + get_region_for(blockDef.modelTextures[i * 6 + 5], assets) + }; + mesh.addBox( + blockDef.modelBoxes[i].center(), + blockDef.modelBoxes[i].size()*0.5f, boxtexfaces + ); + } + const auto& points = blockDef.modelExtraPoints; + glm::vec3 poff = glm::vec3(0.0f, 0.0f, 1.0f); + glm::vec3 norm {0, 1, 0}; + for (size_t i = 0; i < blockDef.modelExtraPoints.size() / 4; i++) { + auto texture = + "blocks:" + + blockDef.modelTextures[blockDef.modelBoxes.size() * 6 + i]; + + auto& mesh = model.addMesh(texture); + mesh.lighting = !def.rt.emissive; + + auto reg = get_region_for(texture, assets); + mesh.vertices.push_back( + {points[i * 4 + 0] - poff, glm::vec2(reg.u1, reg.v1), norm} + ); + mesh.vertices.push_back( + {points[i * 4 + 1] - poff, glm::vec2(reg.u2, reg.v1), norm} + ); + mesh.vertices.push_back( + {points[i * 4 + 2] - poff, glm::vec2(reg.u2, reg.v2), norm} + ); + mesh.vertices.push_back( + {points[i * 4 + 3] - poff, glm::vec2(reg.u1, reg.v1), norm} + ); + mesh.vertices.push_back( + {points[i * 4 + 4] - poff, glm::vec2(reg.u2, reg.v2), norm} + ); + mesh.vertices.push_back( + {points[i * 4 + 0] - poff, glm::vec2(reg.u1, reg.v2), norm} + ); + } } for (auto& mesh : model.meshes) { switch (blockDef.model) { diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index 2d2cdf90..c2ff6ce2 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -116,8 +116,8 @@ public: std::vector modelTextures = {}; std::vector modelBoxes = {}; - std::vector modelExtraPoints = - {}; // initially made for tetragons + // initially made for tetragons + std::vector modelExtraPoints = {}; std::vector modelUVs = {}; // boxes' tex-UVs also there /// @brief id of used BlockMaterial, may specify non-existing material From 0eeb6b6eb2fa6dd9931b73f91df5d20e8f543e4e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 4 Nov 2024 22:48:39 +0300 Subject: [PATCH 23/32] add block particles property --- res/content/base/blocks/torch.json | 18 ++++- res/content/base/scripts/torch.lua | 18 ----- src/content/ContentLoader.cpp | 6 ++ src/frontend/screens/LevelScreen.cpp | 13 +++- src/frontend/screens/LevelScreen.hpp | 2 + src/graphics/render/Decorator.cpp | 103 +++++++++++++++++++++++++++ src/graphics/render/Decorator.hpp | 31 ++++++++ src/presets/ParticlesPreset.hpp | 2 +- src/voxels/Block.cpp | 2 + src/voxels/Block.hpp | 4 ++ src/voxels/Chunks.hpp | 4 ++ 11 files changed, 180 insertions(+), 23 deletions(-) delete mode 100644 res/content/base/scripts/torch.lua create mode 100644 src/graphics/render/Decorator.cpp create mode 100644 src/graphics/render/Decorator.hpp diff --git a/res/content/base/blocks/torch.json b/res/content/base/blocks/torch.json index 133f9739..47f02790 100644 --- a/res/content/base/blocks/torch.json +++ b/res/content/base/blocks/torch.json @@ -15,5 +15,21 @@ "obstacle": false, "grounded": true, "rotation": "pipe", - "material": "base:wood" + "material": "base:wood", + "particles": { + "lifetime": 2.0, + "spawn_interval": 0.3, + "acceleration": [0, 0, 0], + "velocity": [0, 0.3, 0], + "explosion": [0, 0, 0], + "size": [0.2, 0.2, 0.2], + "spawn_shape": "ball", + "spawn_spread": [0.05, 0.05, 0.05], + "lighting": false, + "frames": [ + "particles:fire_0", + "particles:smoke_0", + "particles:smoke_1" + ] + } } diff --git a/res/content/base/scripts/torch.lua b/res/content/base/scripts/torch.lua deleted file mode 100644 index 7a1d2848..00000000 --- a/res/content/base/scripts/torch.lua +++ /dev/null @@ -1,18 +0,0 @@ -function on_placed(x, y, z) - particles.emit({x + 0.5, y + 0.4, z + 0.5}, -1, { - lifetime=2.0, - spawn_interval=0.3, - acceleration={0, 0, 0}, - velocity={0, 0.3, 0}, - explosion={0, 0, 0}, - size={0.2, 0.2, 0.2}, - spawn_shape="ball", - spawn_spread={0.05, 0.05, 0.05}, - lighting=false, - frames={ - "particles:fire_0", - "particles:smoke_0", - "particles:smoke_1" - } - }) -end diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 7bad427e..0e4ef3cf 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -22,6 +22,7 @@ #include "voxels/Block.hpp" #include "data/dv_util.hpp" #include "data/StructLayout.hpp" +#include "presets/ParticlesPreset.hpp" namespace fs = std::filesystem; using namespace data; @@ -335,6 +336,11 @@ void ContentLoader::loadBlock( perform_user_block_fields(def.name, *def.dataStruct); } + if (root.has("particles")) { + def.particles = std::make_unique(); + def.particles->deserialize(root["particles"]); + } + if (def.tickInterval == 0) { def.tickInterval = 1; } diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index b8bb59e6..ad8f35c6 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -14,6 +14,7 @@ #include "graphics/core/PostProcessing.hpp" #include "graphics/core/Viewport.hpp" #include "graphics/render/WorldRenderer.hpp" +#include "graphics/render/Decorator.hpp" #include "graphics/ui/elements/Menu.hpp" #include "graphics/ui/GUI.hpp" #include "logic/LevelController.hpp" @@ -29,20 +30,25 @@ static debug::Logger logger("level-screen"); -LevelScreen::LevelScreen(Engine* engine, std::unique_ptr level) +LevelScreen::LevelScreen(Engine* engine, std::unique_ptr levelPtr) : Screen(engine), postProcessing(std::make_unique()) { + Level* level = levelPtr.get(); + auto& settings = engine->getSettings(); auto assets = engine->getAssets(); auto menu = engine->getGUI()->getMenu(); menu->reset(); - controller = std::make_unique(engine, std::move(level)); + controller = std::make_unique(engine, std::move(levelPtr)); frontend = std::make_unique(controller->getPlayer(), controller.get(), assets); worldRenderer = std::make_unique(engine, frontend.get(), controller->getPlayer()); hud = std::make_unique(engine, frontend.get(), controller->getPlayer()); - + + decorator = + std::make_unique(*level, *worldRenderer->particles, *assets); + keepAlive(settings.graphics.backlight.observe([=](bool) { controller->getLevel()->chunks->saveAndClear(); worldRenderer->clear(); @@ -156,6 +162,7 @@ void LevelScreen::update(float delta) { } controller->update(glm::min(delta, 0.2f), !inputLocked, hud->isPause()); hud->update(hudVisible); + decorator->update(delta, *camera); } void LevelScreen::draw(float delta) { diff --git a/src/frontend/screens/LevelScreen.hpp b/src/frontend/screens/LevelScreen.hpp index d2fe1a58..db0f04ff 100644 --- a/src/frontend/screens/LevelScreen.hpp +++ b/src/frontend/screens/LevelScreen.hpp @@ -12,6 +12,7 @@ class WorldRenderer; class TextureAnimator; class PostProcessing; class ContentPackRuntime; +class Decorator; class Level; class LevelScreen : public Screen { @@ -20,6 +21,7 @@ class LevelScreen : public Screen { std::unique_ptr worldRenderer; std::unique_ptr animator; std::unique_ptr postProcessing; + std::unique_ptr decorator; std::unique_ptr hud; void saveWorldPreview(); diff --git a/src/graphics/render/Decorator.cpp b/src/graphics/render/Decorator.cpp new file mode 100644 index 00000000..51eca386 --- /dev/null +++ b/src/graphics/render/Decorator.cpp @@ -0,0 +1,103 @@ +#include "Decorator.hpp" + +#define GLM_ENABLE_EXPERIMENTAL +#include + +#include "ParticlesRenderer.hpp" +#include "assets/assets_util.hpp" +#include "content/Content.hpp" +#include "voxels/Chunks.hpp" +#include "voxels/Block.hpp" +#include "world/Level.hpp" +#include "window/Camera.hpp" + +/// @brief Not greather than 64 for this BIG_PRIME value +inline constexpr int UPDATE_AREA_DIAMETER = 32; +/// @brief Number of blocks in the volume +inline constexpr int UPDATE_BLOCKS = + UPDATE_AREA_DIAMETER * UPDATE_AREA_DIAMETER * UPDATE_AREA_DIAMETER; +/// @brief Number of update iterations +inline constexpr int ITERATIONS = 512; +/// @brief Big prime number used for pseudo-random 3d array iteration +inline constexpr int BIG_PRIME = 666667; + +Decorator::Decorator( + const Level& level, ParticlesRenderer& particles, const Assets& assets +) + : level(level), particles(particles), assets(assets) { +} + +void Decorator::update( + float delta, const glm::ivec3& areaStart, const glm::ivec3& areaCenter +) { + int index = currentIndex; + currentIndex = (currentIndex + BIG_PRIME) % UPDATE_BLOCKS; + + const auto& chunks = *level.chunks; + const auto& indices = *level.content->getIndices(); + + int lx = index % UPDATE_AREA_DIAMETER; + int lz = (index / UPDATE_AREA_DIAMETER) % UPDATE_AREA_DIAMETER; + int ly = (index / UPDATE_AREA_DIAMETER / UPDATE_AREA_DIAMETER); + + auto pos = areaStart + glm::ivec3(lx, ly, lz); + + if (auto vox = chunks.get(pos)) { + const auto& def = indices.blocks.require(vox->id); + if (def.particles) { + const auto& found = blockEmitters.find(pos); + if (found == blockEmitters.end()) { + auto treg = util::get_texture_region( + assets, def.particles->texture, "" + ); + blockEmitters[pos] = particles.add(std::make_unique( + level, + glm::vec3{pos.x + 0.5, pos.y + 0.5, pos.z + 0.5}, + *def.particles, + treg.texture, + treg.region, + -1 + )); + } + } + } +} + +void Decorator::update(float delta, const Camera& camera) { + glm::ivec3 pos = camera.position; + pos -= glm::ivec3(UPDATE_AREA_DIAMETER / 2); + for (int i = 0; i < ITERATIONS; i++) { + update(delta, pos, camera.position); + } + const auto& chunks = *level.chunks; + const auto& indices = *level.content->getIndices(); + auto iter = blockEmitters.begin(); + while (iter != blockEmitters.end()) { + auto emitter = particles.getEmitter(iter->second); + if (emitter == nullptr) { + iter = blockEmitters.erase(iter); + continue; + } + + bool remove = false; + if (auto vox = chunks.get(iter->first)) { + const auto& def = indices.blocks.require(vox->id); + if (def.particles == nullptr) { + remove = true; + } + } else { + iter = blockEmitters.erase(iter); + continue; + } + if (util::distance2(iter->first, glm::ivec3(camera.position)) > + UPDATE_AREA_DIAMETER * UPDATE_AREA_DIAMETER) { + remove = true; + } + if (remove) { + emitter->stop(); + iter = blockEmitters.erase(iter); + continue; + } + iter++; + } +} diff --git a/src/graphics/render/Decorator.hpp b/src/graphics/render/Decorator.hpp new file mode 100644 index 00000000..83d8a661 --- /dev/null +++ b/src/graphics/render/Decorator.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#define GLM_ENABLE_EXPERIMENTAL +#include + +#include + +class Level; +class Chunks; +class Camera; +class Assets; +class ParticlesRenderer; + +class Decorator { + const Level& level; + const Assets& assets; + ParticlesRenderer& particles; + std::unordered_map blockEmitters; + int currentIndex = 0; + + void update( + float delta, const glm::ivec3& areaStart, const glm::ivec3& areaCenter + ); +public: + Decorator( + const Level& level, ParticlesRenderer& particles, const Assets& assets + ); + + void update(float delta, const Camera& camera); +}; diff --git a/src/presets/ParticlesPreset.hpp b/src/presets/ParticlesPreset.hpp index fcaddb3f..b3fa4be5 100644 --- a/src/presets/ParticlesPreset.hpp +++ b/src/presets/ParticlesPreset.hpp @@ -25,7 +25,7 @@ struct ParticlesPreset : public Serializable { /// @brief Apply lighting bool lighting = true; /// @brief Max distance of actually spawning particles. - float maxDistance = 32.0f; + float maxDistance = 16.0f; /// @brief Particles spawn interval float spawnInterval = 0.1f; /// @brief Particle life time diff --git a/src/voxels/Block.cpp b/src/voxels/Block.cpp index 963a0928..d94f6c82 100644 --- a/src/voxels/Block.cpp +++ b/src/voxels/Block.cpp @@ -5,6 +5,7 @@ #include "core_defs.hpp" #include "data/StructLayout.hpp" +#include "presets/ParticlesPreset.hpp" #include "util/stringutil.hpp" std::string to_string(BlockModel model) { @@ -142,6 +143,7 @@ void Block::cloneTo(Block& dst) { dst.inventorySize = inventorySize; dst.tickInterval = tickInterval; dst.overlayTexture = overlayTexture; + dst.particles = std::make_unique(*particles); } static std::set> RESERVED_BLOCK_FIELDS { diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index c2ff6ce2..d812e48f 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -10,6 +10,8 @@ #include "maths/aabb.hpp" #include "typedefs.hpp" +struct ParticlesPreset; + namespace data { class StructLayout; } @@ -199,6 +201,8 @@ public: std::unique_ptr dataStruct; + std::unique_ptr particles; + /// @brief Runtime indices (content indexing results) struct { /// @brief block runtime integer id diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index 98979423..67746199 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -60,6 +60,10 @@ public: return get(pos.x, pos.y, pos.z); } + inline const voxel* get(glm::ivec3 pos) const { + return get(pos.x, pos.y, pos.z); + } + light_t getLight(int32_t x, int32_t y, int32_t z) const; ubyte getLight(int32_t x, int32_t y, int32_t z, int channel) const; void set(int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state); From 87663e597b61db2812217bf2e8900ed4791aef0c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 5 Nov 2024 01:41:17 +0300 Subject: [PATCH 24/32] add 'global_up_vector' setting --- src/presets/ParticlesPreset.cpp | 2 ++ src/presets/ParticlesPreset.hpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/presets/ParticlesPreset.cpp b/src/presets/ParticlesPreset.cpp index 1414083a..774ab553 100644 --- a/src/presets/ParticlesPreset.cpp +++ b/src/presets/ParticlesPreset.cpp @@ -34,6 +34,7 @@ dv::value ParticlesPreset::serialize() const { root["collision"] = collision; root["lighting"] = lighting; root["max_distance"] = maxDistance; + root["global_up_vector"] = globalUpVector; root["spawn_interval"] = spawnInterval; root["lifetime"] = lifetime; root["lifetime_spread"] = lifetimeSpread; @@ -51,6 +52,7 @@ void ParticlesPreset::deserialize(const dv::value& src) { src.at("texture").get(texture); src.at("collision").get(collision); src.at("lighting").get(lighting); + src.at("global_up_vector").get(globalUpVector); src.at("max_distance").get(maxDistance); src.at("spawn_interval").get(spawnInterval); src.at("lifetime").get(lifetime); diff --git a/src/presets/ParticlesPreset.hpp b/src/presets/ParticlesPreset.hpp index b3fa4be5..262b2c07 100644 --- a/src/presets/ParticlesPreset.hpp +++ b/src/presets/ParticlesPreset.hpp @@ -24,6 +24,8 @@ struct ParticlesPreset : public Serializable { bool collision = true; /// @brief Apply lighting bool lighting = true; + /// @brief Use global up vector instead of camera-dependent one + bool globalUpVector = false; /// @brief Max distance of actually spawning particles. float maxDistance = 16.0f; /// @brief Particles spawn interval From 6f9bad05572c7ec394f8bae7da2cf93c0c50ce7e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 5 Nov 2024 01:51:37 +0300 Subject: [PATCH 25/32] fix block models generation --- res/shaders/entity.glslf | 2 +- src/graphics/render/ModelBatch.cpp | 21 +++++++++++++++------ src/graphics/render/ModelsGenerator.cpp | 9 +++++++-- src/graphics/render/ParticlesRenderer.cpp | 2 +- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/res/shaders/entity.glslf b/res/shaders/entity.glslf index b332c02f..5da4142c 100644 --- a/res/shaders/entity.glslf +++ b/res/shaders/entity.glslf @@ -16,7 +16,7 @@ void main() { float depth = (a_distance/256.0); float alpha = a_color.a * tex_color.a; // anyway it's any alpha-test alternative required - if (alpha < 0.9f) + if (alpha < 0.5f) discard; f_color = mix(a_color * tex_color, vec4(fogColor,1.0), min(1.0, pow(depth*u_fogFactor, u_fogCurve))); diff --git a/src/graphics/render/ModelBatch.cpp b/src/graphics/render/ModelBatch.cpp index e222b349..42ceb921 100644 --- a/src/graphics/render/ModelBatch.cpp +++ b/src/graphics/render/ModelBatch.cpp @@ -63,19 +63,28 @@ void ModelBatch::draw(const model::Mesh& mesh, const glm::mat4& matrix, const glm::mat3& rotation, glm::vec3 tint, const texture_names_map* varTextures, bool backlight) { - glm::vec3 gpos = matrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); - gpos += lightsOffset; - glm::vec4 lights = MainBatch::sampleLight(gpos, *chunks, backlight); + + setTexture(mesh.texture, varTextures); size_t vcount = mesh.vertices.size(); const auto& vertexData = mesh.vertices.data(); + + glm::vec4 lights(1, 1, 1, 0); + if (mesh.lighting) { + glm::vec3 gpos = matrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); + gpos += lightsOffset; + lights = MainBatch::sampleLight(gpos, *chunks, backlight); + } for (size_t i = 0; i < vcount / 3; i++) { batch->prepare(3); for (size_t j = 0; j < 3; j++) { const auto vert = vertexData[i * 3 + j]; - auto norm = rotation * vert.normal; - float d = glm::dot(norm, SUN_VECTOR); - d = 0.8f + d * 0.2f; + float d = 1.0f; + if (mesh.lighting) { + auto norm = rotation * vert.normal; + d = glm::dot(norm, SUN_VECTOR); + d = 0.8f + d * 0.2f; + } batch->vertex(matrix * glm::vec4(vert.coord, 1.0f), vert.uv, lights*d, tint); } } diff --git a/src/graphics/render/ModelsGenerator.cpp b/src/graphics/render/ModelsGenerator.cpp index 07bf7b0d..705cabd3 100644 --- a/src/graphics/render/ModelsGenerator.cpp +++ b/src/graphics/render/ModelsGenerator.cpp @@ -63,7 +63,7 @@ model::Model ModelsGenerator::generate( for (size_t i = 0; i < blockDef.modelBoxes.size(); i++) { auto& mesh = model.addMesh("blocks:" + blockDef.modelTextures[i * 6]); - mesh.lighting = !def.rt.emissive; + mesh.lighting = !blockDef.shadeless; const UVRegion (&boxtexfaces)[6] = { get_region_for(blockDef.modelTextures[i * 6], assets), get_region_for(blockDef.modelTextures[i * 6 + 1], assets), @@ -86,7 +86,7 @@ model::Model ModelsGenerator::generate( blockDef.modelTextures[blockDef.modelBoxes.size() * 6 + i]; auto& mesh = model.addMesh(texture); - mesh.lighting = !def.rt.emissive; + mesh.lighting = !blockDef.shadeless; auto reg = get_region_for(texture, assets); mesh.vertices.push_back( @@ -108,8 +108,13 @@ model::Model ModelsGenerator::generate( {points[i * 4 + 0] - poff, glm::vec2(reg.u1, reg.v2), norm} ); } + for (auto& mesh : model.meshes) { + mesh.scale(glm::vec3(0.3f)); + } + return model; } for (auto& mesh : model.meshes) { + mesh.lighting = !blockDef.shadeless; switch (blockDef.model) { case BlockModel::aabb: { glm::vec3 size = blockDef.hitboxes.at(0).size(); diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index d377773e..0e030dbc 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -92,7 +92,7 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { batch->quad( particle.position, right, - up, + preset.globalUpVector ? glm::vec3(0, 1, 0) : up, preset.size, light, glm::vec3(1.0f), From 6dfbd5f401456b2b25e0e26bbca0164c9da17a62 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 5 Nov 2024 02:01:24 +0300 Subject: [PATCH 26/32] add experimental optimization --- src/graphics/render/WorldRenderer.cpp | 33 +++++++++++++++------------ 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 89b5474b..d1391ca4 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -106,16 +106,6 @@ bool WorldRenderer::drawChunk( if (mesh == nullptr) { return false; } - if (culling) { - glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D); - glm::vec3 max( - chunk->x * CHUNK_W + CHUNK_W, - chunk->top, - chunk->z * CHUNK_D + CHUNK_D - ); - - if (!frustumCulling->isBoxVisible(min, max)) return false; - } glm::vec3 coord(chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f); glm::mat4 model = glm::translate(glm::mat4(1.0f), coord); shader->uniformMatrix("u_model", model); @@ -134,9 +124,25 @@ void WorldRenderer::drawChunks( // [warning] this whole method is not thread-safe for chunks + bool culling = engine->getSettings().graphics.frustumCulling.get(); + if (culling) { + frustumCulling->update(camera.getProjView()); + } + std::vector indices; for (size_t i = 0; i < chunks->getVolume(); i++) { - if (chunks->getChunks()[i] == nullptr) continue; + auto chunk = chunks->getChunks()[i]; + if (chunk == nullptr) continue; + if (culling) { + glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D); + glm::vec3 max( + chunk->x * CHUNK_W + CHUNK_W, + chunk->top, + chunk->z * CHUNK_D + CHUNK_D + ); + + if (!frustumCulling->isBoxVisible(min, max)) continue; + } indices.emplace_back(i); } float px = camera.position.x / static_cast(CHUNK_W) - 0.5f; @@ -151,10 +157,7 @@ void WorldRenderer::drawChunks( auto bdz = (b->z - pz); return (adx * adx + adz * adz > bdx * bdx + bdz * bdz); }); - bool culling = engine->getSettings().graphics.frustumCulling.get(); - if (culling) { - frustumCulling->update(camera.getProjView()); - } + chunks->visible = 0; for (size_t i = 0; i < indices.size(); i++) { chunks->visible += drawChunk(indices[i], camera, shader, culling); From fb41b932fa23e7d0d5794f19b6460d0816eb4e2b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 5 Nov 2024 02:10:10 +0300 Subject: [PATCH 27/32] Revert "add experimental optimization" This reverts commit 6dfbd5f401456b2b25e0e26bbca0164c9da17a62. --- src/graphics/render/WorldRenderer.cpp | 33 ++++++++++++--------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index d1391ca4..89b5474b 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -106,6 +106,16 @@ bool WorldRenderer::drawChunk( if (mesh == nullptr) { return false; } + if (culling) { + glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D); + glm::vec3 max( + chunk->x * CHUNK_W + CHUNK_W, + chunk->top, + chunk->z * CHUNK_D + CHUNK_D + ); + + if (!frustumCulling->isBoxVisible(min, max)) return false; + } glm::vec3 coord(chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f); glm::mat4 model = glm::translate(glm::mat4(1.0f), coord); shader->uniformMatrix("u_model", model); @@ -124,25 +134,9 @@ void WorldRenderer::drawChunks( // [warning] this whole method is not thread-safe for chunks - bool culling = engine->getSettings().graphics.frustumCulling.get(); - if (culling) { - frustumCulling->update(camera.getProjView()); - } - std::vector indices; for (size_t i = 0; i < chunks->getVolume(); i++) { - auto chunk = chunks->getChunks()[i]; - if (chunk == nullptr) continue; - if (culling) { - glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D); - glm::vec3 max( - chunk->x * CHUNK_W + CHUNK_W, - chunk->top, - chunk->z * CHUNK_D + CHUNK_D - ); - - if (!frustumCulling->isBoxVisible(min, max)) continue; - } + if (chunks->getChunks()[i] == nullptr) continue; indices.emplace_back(i); } float px = camera.position.x / static_cast(CHUNK_W) - 0.5f; @@ -157,7 +151,10 @@ void WorldRenderer::drawChunks( auto bdz = (b->z - pz); return (adx * adx + adz * adz > bdx * bdx + bdz * bdz); }); - + bool culling = engine->getSettings().graphics.frustumCulling.get(); + if (culling) { + frustumCulling->update(camera.getProjView()); + } chunks->visible = 0; for (size_t i = 0; i < indices.size(); i++) { chunks->visible += drawChunk(indices[i], camera, shader, culling); From f5bc6fc1368aac5279bac1f620e71dc216261b72 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 5 Nov 2024 03:23:13 +0300 Subject: [PATCH 28/32] remove extra include --- src/graphics/render/Decorator.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/graphics/render/Decorator.cpp b/src/graphics/render/Decorator.cpp index 51eca386..eb2255a6 100644 --- a/src/graphics/render/Decorator.cpp +++ b/src/graphics/render/Decorator.cpp @@ -1,8 +1,5 @@ #include "Decorator.hpp" -#define GLM_ENABLE_EXPERIMENTAL -#include - #include "ParticlesRenderer.hpp" #include "assets/assets_util.hpp" #include "content/Content.hpp" From 77ae35e3641088001c9e465b5cc0734186677c35 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 5 Nov 2024 03:46:41 +0300 Subject: [PATCH 29/32] feat: make Decorator listen to block interactions --- src/frontend/LevelFrontend.cpp | 2 +- src/frontend/screens/LevelScreen.cpp | 5 ++-- src/graphics/render/Decorator.cpp | 43 +++++++++++++++++----------- src/graphics/render/Decorator.hpp | 5 +++- src/logic/BlocksController.hpp | 2 +- 5 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/frontend/LevelFrontend.cpp b/src/frontend/LevelFrontend.cpp index 6c54a83d..977a0350 100644 --- a/src/frontend/LevelFrontend.cpp +++ b/src/frontend/LevelFrontend.cpp @@ -25,7 +25,7 @@ LevelFrontend::LevelFrontend( "block-previews" ); controller->getBlocksController()->listenBlockInteraction( - [=](Player* player, glm::ivec3 pos, const Block& def, BlockInteraction type) { + [=](auto player, const auto& pos, const auto& def, BlockInteraction type) { auto material = level->content->findBlockMaterial(def.material); if (material == nullptr) { return; diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index ad8f35c6..2dda016c 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -46,8 +46,9 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr levelPtr) worldRenderer = std::make_unique(engine, frontend.get(), controller->getPlayer()); hud = std::make_unique(engine, frontend.get(), controller->getPlayer()); - decorator = - std::make_unique(*level, *worldRenderer->particles, *assets); + decorator = std::make_unique( + *controller, *worldRenderer->particles, *assets + ); keepAlive(settings.graphics.backlight.observe([=](bool) { controller->getLevel()->chunks->saveAndClear(); diff --git a/src/graphics/render/Decorator.cpp b/src/graphics/render/Decorator.cpp index eb2255a6..4bac77af 100644 --- a/src/graphics/render/Decorator.cpp +++ b/src/graphics/render/Decorator.cpp @@ -7,6 +7,7 @@ #include "voxels/Block.hpp" #include "world/Level.hpp" #include "window/Camera.hpp" +#include "logic/LevelController.hpp" /// @brief Not greather than 64 for this BIG_PRIME value inline constexpr int UPDATE_AREA_DIAMETER = 32; @@ -19,9 +20,32 @@ inline constexpr int ITERATIONS = 512; inline constexpr int BIG_PRIME = 666667; Decorator::Decorator( - const Level& level, ParticlesRenderer& particles, const Assets& assets + LevelController& controller, ParticlesRenderer& particles, const Assets& assets ) - : level(level), particles(particles), assets(assets) { + : level(*controller.getLevel()), particles(particles), assets(assets) { + controller.getBlocksController()->listenBlockInteraction( + [this](auto player, const auto& pos, const auto& def, BlockInteraction type) { + if (type == BlockInteraction::placing && def.particles) { + addParticles(def, pos); + } + }); +} + +void Decorator::addParticles(const Block& def, const glm::ivec3& pos) { + const auto& found = blockEmitters.find(pos); + if (found == blockEmitters.end()) { + auto treg = util::get_texture_region( + assets, def.particles->texture, "" + ); + blockEmitters[pos] = particles.add(std::make_unique( + level, + glm::vec3{pos.x + 0.5, pos.y + 0.5, pos.z + 0.5}, + *def.particles, + treg.texture, + treg.region, + -1 + )); + } } void Decorator::update( @@ -42,20 +66,7 @@ void Decorator::update( if (auto vox = chunks.get(pos)) { const auto& def = indices.blocks.require(vox->id); if (def.particles) { - const auto& found = blockEmitters.find(pos); - if (found == blockEmitters.end()) { - auto treg = util::get_texture_region( - assets, def.particles->texture, "" - ); - blockEmitters[pos] = particles.add(std::make_unique( - level, - glm::vec3{pos.x + 0.5, pos.y + 0.5, pos.z + 0.5}, - *def.particles, - treg.texture, - treg.region, - -1 - )); - } + addParticles(def, pos); } } } diff --git a/src/graphics/render/Decorator.hpp b/src/graphics/render/Decorator.hpp index 83d8a661..32f9540a 100644 --- a/src/graphics/render/Decorator.hpp +++ b/src/graphics/render/Decorator.hpp @@ -10,6 +10,8 @@ class Level; class Chunks; class Camera; class Assets; +struct Block; +class LevelController; class ParticlesRenderer; class Decorator { @@ -22,9 +24,10 @@ class Decorator { void update( float delta, const glm::ivec3& areaStart, const glm::ivec3& areaCenter ); + void addParticles(const Block& def, const glm::ivec3& pos); public: Decorator( - const Level& level, ParticlesRenderer& particles, const Assets& assets + LevelController& level, ParticlesRenderer& particles, const Assets& assets ); void update(float delta, const Camera& camera); diff --git a/src/logic/BlocksController.hpp b/src/logic/BlocksController.hpp index 19a270f0..86bbe395 100644 --- a/src/logic/BlocksController.hpp +++ b/src/logic/BlocksController.hpp @@ -20,7 +20,7 @@ enum class BlockInteraction { step, destruction, placing }; /// @brief Player argument is nullable using on_block_interaction = std::function< - void(Player*, glm::ivec3, const Block&, BlockInteraction type)>; + void(Player*, const glm::ivec3&, const Block&, BlockInteraction type)>; /// BlocksController manages block updates and data (inventories, metadata) class BlocksController { From a4f7dbf786012f100eafed926d2a2b37dc758d3f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 5 Nov 2024 03:49:22 +0300 Subject: [PATCH 30/32] feat: make emitter spawn first particle just on first update --- src/graphics/render/Emitter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/graphics/render/Emitter.cpp b/src/graphics/render/Emitter.cpp index 046d6486..3418f09d 100644 --- a/src/graphics/render/Emitter.cpp +++ b/src/graphics/render/Emitter.cpp @@ -24,6 +24,7 @@ Emitter::Emitter( count(count), preset(std::move(preset)) { this->prototype.emitter = this; + timer = preset.spawnInterval; } const Texture* Emitter::getTexture() const { From edb4ce02ca69076b4c438a591c94dec926257ee2 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 5 Nov 2024 04:08:55 +0300 Subject: [PATCH 31/32] add particles.get_origin(...), particles.set_origin(...) --- src/graphics/render/Emitter.cpp | 8 +++++ src/graphics/render/Emitter.hpp | 4 +++ src/logic/scripting/lua/libs/libparticles.cpp | 29 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/src/graphics/render/Emitter.cpp b/src/graphics/render/Emitter.cpp index 3418f09d..582410db 100644 --- a/src/graphics/render/Emitter.cpp +++ b/src/graphics/render/Emitter.cpp @@ -113,3 +113,11 @@ void Emitter::stop() { bool Emitter::isDead() const { return count == 0; } + +const EmitterOrigin& Emitter::getOrigin() const { + return origin; +} + +void Emitter::setOrigin(const EmitterOrigin& origin) { + this->origin = origin; +} diff --git a/src/graphics/render/Emitter.hpp b/src/graphics/render/Emitter.hpp index 24867edd..c1d6c168 100644 --- a/src/graphics/render/Emitter.hpp +++ b/src/graphics/render/Emitter.hpp @@ -81,4 +81,8 @@ public: /// @return true if the emitter has spawned all particles bool isDead() const; + + const EmitterOrigin& getOrigin() const; + + void setOrigin(const EmitterOrigin& origin); }; diff --git a/src/logic/scripting/lua/libs/libparticles.cpp b/src/logic/scripting/lua/libs/libparticles.cpp index 9b488d7a..bd769a15 100644 --- a/src/logic/scripting/lua/libs/libparticles.cpp +++ b/src/logic/scripting/lua/libs/libparticles.cpp @@ -46,8 +46,37 @@ static int l_stop(lua::State* L) { return 0; } +static int l_get_origin(lua::State* L) { + u64id_t id = lua::touinteger(L, 1); + if (auto emitter = renderer->particles->getEmitter(id)) { + const auto& origin = emitter->getOrigin(); + if (auto pos = std::get_if(&origin)) { + return lua::pushvec3(L, *pos); + } else if (auto entityid = std::get_if(&origin)) { + return lua::pushinteger(L, *entityid); + } + } + return 0; +} + +static int l_set_origin(lua::State* L) { + u64id_t id = lua::touinteger(L, 1); + if (auto emitter = renderer->particles->getEmitter(id)) { + EmitterOrigin origin; + if (lua::istable(L, 2)) { + emitter->setOrigin(lua::tovec3(L, 2)); + } else { + emitter->setOrigin(static_cast(lua::tointeger(L, 2))); + } + } + return 0; +} + + const luaL_Reg particleslib[] = { {"emit", lua::wrap}, {"stop", lua::wrap}, + {"get_origin", lua::wrap}, + {"set_origin", lua::wrap}, {NULL, NULL} }; From f95b743a6cc538360478fc47e76ea143e3aef04b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 5 Nov 2024 04:10:56 +0300 Subject: [PATCH 32/32] add particles.is_alive --- src/logic/scripting/lua/libs/libparticles.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/logic/scripting/lua/libs/libparticles.cpp b/src/logic/scripting/lua/libs/libparticles.cpp index bd769a15..ae2ed6d3 100644 --- a/src/logic/scripting/lua/libs/libparticles.cpp +++ b/src/logic/scripting/lua/libs/libparticles.cpp @@ -72,10 +72,18 @@ static int l_set_origin(lua::State* L) { return 0; } +static int l_is_alive(lua::State* L) { + u64id_t id = lua::touinteger(L, 1); + if (auto emitter = renderer->particles->getEmitter(id)) { + return lua::pushboolean(L, !emitter->isDead()); + } + return lua::pushboolean(L, false); +} const luaL_Reg particleslib[] = { {"emit", lua::wrap}, {"stop", lua::wrap}, + {"is_alive", lua::wrap}, {"get_origin", lua::wrap}, {"set_origin", lua::wrap}, {NULL, NULL}