#include "Emitter.hpp" #include #define GLM_ENABLE_EXPERIMENTAL #include #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, ParticlesPreset preset, const Texture* texture, const UVRegion& region, int count ) : level(level), origin(std::move(origin)), prototype({this, 0, glm::vec3(), preset.velocity, preset.lifetime, region}), texture(texture), count(count), preset(std::move(preset)) { this->prototype.emitter = this; } 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, std::vector& particles ) { const float spawnInterval = preset.spawnInterval; if (count == 0 || (count == -1 && spawnInterval < FLT_EPSILON)) { return; } glm::vec3 position {}; if (auto staticPos = std::get_if(&origin)) { position = *staticPos; } 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; 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 Particle particle = prototype; particle.emitter = this; particle.random = random.rand32(); 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) { particle.region.autoSub( preset.randomSubUV, preset.randomSubUV, random.randFloat(), random.randFloat() ); } particles.push_back(std::move(particle)); timer -= spawnInterval; if (count > 0) { count--; } } } void Emitter::stop() { count = 0; } bool Emitter::isDead() const { return count == 0; }