#pragma once #include "render/Renderer.h" #include "render/TextureManager.h" #include #include #include namespace ZL { struct SparkParticle { Vector3f position; Vector3f velocity; float scale; float lifeTime; float maxLifeTime; bool active; int emitterIndex; SparkParticle() : position({ 0,0,0 }), velocity({ 0,0,0 }), scale(1.0f), lifeTime(0), maxLifeTime(1000.0f), active(false), emitterIndex(0) { } }; class SparkEmitter { private: std::vector particles; std::vector emissionPoints; std::vector prevEmissionPoints; std::chrono::steady_clock::time_point lastEmissionTime; float emissionRate; bool isActive; std::vector drawPositions; std::vector drawTexCoords; bool drawDataDirty; VertexRenderStruct sparkQuad; std::shared_ptr texture; int maxParticles; float particleSize; float biasX; // Ranges (used when config supplies intervals) struct FloatRange { float min=0; float max=0; }; FloatRange speedRange; // XY speed FloatRange zSpeedRange; // Z speed FloatRange scaleRange; FloatRange lifeTimeRange; std::string shaderProgramName; // Sparks spawn within this radius of each emission point. 0 means // "spawn exactly at the point" (legacy behavior). float emissionRadius; bool configured; // Rebuilds the CPU-side quad/UV arrays. If cameraViewMatrix is identity, // particles are rendered as axis-aligned quads in world XY and sorted by // world Z (legacy behavior). Otherwise quads are billboarded toward the // camera using its world-space right/up axes, and particles are sorted // back-to-front in eye space for correct alpha blending. void prepareDrawData(const Eigen::Matrix4f& cameraViewMatrix); bool useWorldSpace; // When non-zero, velocities spray in a cone around this direction // instead of the default planar XY + Z spread used by JSON emitters. Vector3f emissionDirection = Vector3f{ 0.f, 0.f, 0.f }; public: SparkEmitter(); SparkEmitter(const SparkEmitter& copyFrom); SparkEmitter(const std::vector& positions, float rate = 100.0f); SparkEmitter(const std::vector& positions, std::shared_ptr tex, float rate = 100.0f); void setEmissionPoints(const std::vector& positions); void resetEmissionPoints(const std::vector& positions); void setTexture(std::shared_ptr tex); void setEmissionRate(float rate) { emissionRate = rate; } void setShaderProgramName(const std::string& name) { shaderProgramName = name; } void setMaxParticles(int max) { maxParticles = max; particles.resize(maxParticles); } void setParticleSize(float size) { particleSize = size; } void setBiasX(float bias) { biasX = bias; } void setUseWorldSpace(bool use) { useWorldSpace = use; } bool loadFromJsonFile(const std::string& path, Renderer& renderer, const std::string& zipFile = ""); // Programmatic configuration (alternative to loadFromJsonFile). // Ranges follow the same semantics as the JSON fields. void setSpeedRange(float minVal, float maxVal) { speedRange.min = minVal; speedRange.max = maxVal; } void setZSpeedRange(float minVal, float maxVal) { zSpeedRange.min = minVal; zSpeedRange.max = maxVal; } void setScaleRange(float minVal, float maxVal) { scaleRange.min = minVal; scaleRange.max = maxVal; } void setLifeTimeRange(float minVal, float maxVal) { lifeTimeRange.min = minVal; lifeTimeRange.max = maxVal; } void setEmissionDirection(const Vector3f& dir) { emissionDirection = dir; } // 0 = spawn exactly at the emission point; >0 = spawn at a uniform random // point inside a 3D ball of this radius around the emission point. void setEmissionRadius(float r) { emissionRadius = r; } void markConfigured() { configured = true; } bool isConfigured() const { return configured; } void update(float deltaTimeMs); void emit(float ageMs = 0.0f, float lerpT = 0.0f); // Вызывать ДО draw() в начале кадра: готовит данные и загружает в VBO. // Pass the current camera view matrix (world → eye) to billboard sparks // toward the camera and sort them back-to-front. Identity (default) // keeps the legacy non-billboarded behavior. void prepareForDraw(const Eigen::Matrix4f& cameraViewMatrix = Eigen::Matrix4f::Identity()); void draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight); void draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight, bool withRotation); const std::vector& getParticles() const; size_t getActiveParticleCount() const; std::shared_ptr getTexture() const { return texture; } void setIsActive(bool newActive) { isActive = newActive; } private: void initParticle(SparkParticle& particle, int emitterIndex); Vector3f getRandomVelocity(int emitterIndex); }; } // namespace ZL