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);