add particles (WIP)

This commit is contained in:
MihailRis 2024-11-02 20:40:40 +03:00
parent a0f885e26a
commit 217176f74f
8 changed files with 280 additions and 4 deletions

View File

@ -0,0 +1,53 @@
#include "Emitter.hpp"
#include <glm/gtc/random.hpp>
#include "graphics/core/Texture.hpp"
Emitter::Emitter(
std::variant<glm::vec3, entityid_t> 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<Particle>& particles) {
if (count == 0) {
return;
}
glm::vec3 position {};
if (auto staticPos = std::get_if<glm::vec3>(&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;
}

View File

@ -0,0 +1,56 @@
#pragma once
#include <vector>
#include <variant>
#include <glm/glm.hpp>
#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<glm::vec3, entityid_t> 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<glm::vec3, entityid_t> 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<Particle>& particles);
void setExplosion(const glm::vec3& magnitude);
bool isDead() const;
};

View File

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

View File

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

View File

@ -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<MainBatch>(1024)) {
Emitter emitter(glm::vec3(0, 100, 0), Particle {
glm::vec3(), glm::vec3(), 5.0f, UVRegion(0,0,1,1)
}, assets.get<Texture>("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<Particle>* 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++;
}
}
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <vector>
#include <unordered_map>
#include "Emitter.hpp"
class Texture;
class Assets;
class Camera;
class MainBatch;
class ParticlesRenderer {
std::unordered_map<const Texture*, std::vector<Particle>> particles;
std::vector<Emitter> emitters;
std::unique_ptr<MainBatch> batch;
public:
ParticlesRenderer(const Assets& assets);
~ParticlesRenderer();
void render(const Assets& assets, const Camera& camera, float delta);
};

View File

@ -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<Frustum>()),
lineBatch(std::make_unique<LineBatch>()),
modelBatch(std::make_unique<ModelBatch>(
20'000, engine->getAssets(), level->chunks.get(),
20'000,
engine->getAssets(),
level->chunks.get(),
&engine->getSettings()
)) {
)),
particles(std::make_unique<ParticlesRenderer>(*engine->getAssets())) {
renderer = std::make_unique<ChunksRenderer>(
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<Shader>("main");

View File

@ -8,12 +8,15 @@
#include <glm/glm.hpp>
#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> skybox;
std::unique_ptr<Batch3D> batch3d;
std::unique_ptr<ModelBatch> modelBatch;
std::unique_ptr<ParticlesRenderer> particles;
float timer = 0.0f;
bool drawChunk(size_t index, const Camera& camera, Shader* shader, bool culling);