refactor(效果系统): 移除效果相关代码

移除后处理、粒子系统和自定义效果管理器的头文件和实现文件
删除extra2d.h中对效果模块的包含
This commit is contained in:
ChestnutYueyue 2026-02-25 06:27:58 +08:00
parent f08a0bf583
commit f9c8f080eb
8 changed files with 0 additions and 6234 deletions

View File

@ -1,324 +0,0 @@
#pragma once
#include <core/color.h>
#include <core/types.h>
#include <effects/particle_system.h>
#include <effects/post_process.h>
#include <graphics/shader_system.h>
#include <functional>
#include <string>
#include <unordered_map>
#include <vector>
namespace extra2d {
// ============================================================================
// 自定义特效类型
// ============================================================================
enum class CustomEffectType {
Particle, // 粒子特效
PostProcess, // 后处理特效
Shader, // Shader特效
Combined // 组合特效
};
// ============================================================================
// 自定义特效配置
// ============================================================================
struct CustomEffectConfig {
std::string name; // 特效名称
CustomEffectType type; // 特效类型
std::string description; // 描述
// 粒子特效配置
EmitterConfig emitterConfig;
// 后处理特效配置
std::string shaderVertPath; // 顶点着色器路径
std::string shaderFragPath; // 片段着色器路径
std::unordered_map<std::string, float> shaderParams; // Shader参数
// 通用配置
float duration; // 持续时间(-1表示无限)
bool loop; // 是否循环
float delay; // 延迟启动时间
};
// ============================================================================
// 自定义特效基类
// ============================================================================
class CustomEffect {
public:
explicit CustomEffect(const CustomEffectConfig &config);
virtual ~CustomEffect() = default;
// ------------------------------------------------------------------------
// 生命周期
// ------------------------------------------------------------------------
virtual bool init();
virtual void update(float dt);
virtual void render(RenderBackend &renderer);
virtual void shutdown();
// ------------------------------------------------------------------------
// 控制
// ------------------------------------------------------------------------
void play();
void pause();
void stop();
void reset();
bool isPlaying() const { return playing_; }
bool isFinished() const { return finished_; }
float getElapsedTime() const { return elapsedTime_; }
// ------------------------------------------------------------------------
// 配置
// ------------------------------------------------------------------------
const std::string &getName() const { return config_.name; }
const CustomEffectConfig &getConfig() const { return config_; }
void setPosition(const Vec2 &pos) { position_ = pos; }
void setRotation(float rot) { rotation_ = rot; }
void setScale(float scale) { scale_ = scale; }
Vec2 getPosition() const { return position_; }
float getRotation() const { return rotation_; }
float getScale() const { return scale_; }
protected:
CustomEffectConfig config_;
Vec2 position_ = Vec2::Zero();
float rotation_ = 0.0f;
float scale_ = 1.0f;
bool playing_ = false;
bool paused_ = false;
bool finished_ = false;
float elapsedTime_ = 0.0f;
float delayTimer_ = 0.0f;
};
// ============================================================================
// 自定义粒子特效
// ============================================================================
class CustomParticleEffect : public CustomEffect {
public:
explicit CustomParticleEffect(const CustomEffectConfig &config);
bool init() override;
void update(float dt) override;
void render(RenderBackend &renderer) override;
void shutdown() override;
void play();
void stop();
Ptr<ParticleEmitter> getEmitter() { return emitter_; }
private:
Ptr<ParticleSystem> particleSystem_;
Ptr<ParticleEmitter> emitter_;
};
// ============================================================================
// 自定义后处理特效
// ============================================================================
class CustomPostProcessEffect : public CustomEffect, public PostProcessEffect {
public:
explicit CustomPostProcessEffect(const CustomEffectConfig &config);
bool init() override;
void update(float dt) override;
void shutdown() override;
void onShaderBind(GLShader &shader) override;
void setParam(const std::string &name, float value);
float getParam(const std::string &name) const;
private:
std::unordered_map<std::string, float> runtimeParams_;
};
// ============================================================================
// 自定义特效工厂
// ============================================================================
class CustomEffectFactory {
public:
using EffectCreator =
std::function<Ptr<CustomEffect>(const CustomEffectConfig &)>;
static CustomEffectFactory &getInstance();
// 注册自定义特效创建器
void registerEffect(const std::string &typeName, EffectCreator creator);
// 创建特效
Ptr<CustomEffect> create(const std::string &typeName,
const CustomEffectConfig &config);
// 检查是否已注册
bool isRegistered(const std::string &typeName) const;
// 获取所有已注册的类型
std::vector<std::string> getRegisteredTypes() const;
private:
CustomEffectFactory() = default;
~CustomEffectFactory() = default;
CustomEffectFactory(const CustomEffectFactory &) = delete;
CustomEffectFactory &operator=(const CustomEffectFactory &) = delete;
std::unordered_map<std::string, EffectCreator> creators_;
};
// ============================================================================
// 自定义特效管理器
// ============================================================================
class CustomEffectManager {
public:
static CustomEffectManager &getInstance();
// ------------------------------------------------------------------------
// 初始化和关闭
// ------------------------------------------------------------------------
bool init();
void shutdown();
// ------------------------------------------------------------------------
// 特效管理
// ------------------------------------------------------------------------
/**
* @brief JSON和文本格式
* JSON格式优先退
*/
bool loadFromFile(const std::string &filepath);
/**
* @brief
* EMISSION 100
*/
bool loadFromTextFile(const std::string &filepath);
/**
* @brief
* @param useJson true=JSON格式, false=
*/
bool saveToFile(const std::string &name, const std::string &filepath,
bool useJson = true);
/**
* @brief JSON文件
*/
bool saveAllToFile(const std::string &filepath);
/**
* @brief
*/
void registerConfig(const std::string &name,
const CustomEffectConfig &config);
/**
* @brief
*/
CustomEffectConfig *getConfig(const std::string &name);
/**
* @brief
*/
void removeConfig(const std::string &name);
/**
* @brief
*/
std::vector<std::string> getConfigNames() const;
// ------------------------------------------------------------------------
// 特效实例管理
// ------------------------------------------------------------------------
/**
* @brief
*/
Ptr<CustomEffect> createEffect(const std::string &name);
/**
* @brief
*/
Ptr<CustomEffect> createEffectFromConfig(const CustomEffectConfig &config);
/**
* @brief
*/
void destroyEffect(Ptr<CustomEffect> effect);
/**
* @brief
*/
void update(float dt);
/**
* @brief
*/
void render(RenderBackend &renderer);
/**
* @brief
*/
void stopAll();
// ------------------------------------------------------------------------
// 便捷方法
// ------------------------------------------------------------------------
/**
* @brief
*/
Ptr<CustomEffect> play(const std::string &name, const Vec2 &position);
/**
* @brief
*/
void playOneShot(const std::string &name, const Vec2 &position);
private:
CustomEffectManager() = default;
~CustomEffectManager() = default;
CustomEffectManager(const CustomEffectManager &) = delete;
CustomEffectManager &operator=(const CustomEffectManager &) = delete;
std::unordered_map<std::string, CustomEffectConfig> configs_;
std::vector<Ptr<CustomEffect>> activeEffects_;
};
// ============================================================================
// 便捷宏
// ============================================================================
#define E2D_CUSTOM_EFFECT_MANAGER() \
::extra2d::CustomEffectManager::getInstance()
#define E2D_CUSTOM_EFFECT_FACTORY() \
::extra2d::CustomEffectFactory::getInstance()
// ============================================================================
// 预设特效快速创建
// ============================================================================
class EffectBuilder {
public:
// 粒子特效
static CustomEffectConfig Particle(const std::string &name);
static CustomEffectConfig Fire(const std::string &name);
static CustomEffectConfig Smoke(const std::string &name);
static CustomEffectConfig Explosion(const std::string &name);
static CustomEffectConfig Magic(const std::string &name);
static CustomEffectConfig Sparkle(const std::string &name);
// 后处理特效
static CustomEffectConfig Bloom(const std::string &name);
static CustomEffectConfig Blur(const std::string &name);
static CustomEffectConfig Vignette(const std::string &name);
static CustomEffectConfig ColorGrading(const std::string &name);
};
} // namespace extra2d

View File

@ -1,300 +0,0 @@
#pragma once
#include <core/color.h>
#include <core/types.h>
#include <graphics/texture.h>
#include <scene/node.h>
#include <functional>
#include <random>
#include <vector>
namespace extra2d {
// ============================================================================
// 快速随机数生成器 - 使用 xorshift 算法,比 std::mt19937 更快
// ============================================================================
class FastRNG {
public:
explicit FastRNG(uint32_t seed = 0) : state_(seed ? seed : 0x853c49e67) {}
float nextFloat() {
return static_cast<float>(next()) / static_cast<float>(UINT32_MAX);
}
float nextFloat(float min, float max) {
return min + (max - min) * nextFloat();
}
private:
uint32_t state_;
uint32_t next() {
uint32_t x = state_;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
state_ = x;
return x;
}
};
// ============================================================================
// 粒子数据
// ============================================================================
struct Particle {
Vec2 position;
Vec2 velocity;
Vec2 acceleration;
float rotation;
float angularVelocity;
float size;
float sizeDelta;
Color color;
Color colorDelta;
float life;
float maxLife;
bool active;
Particle()
: position(Vec2::Zero()), velocity(Vec2::Zero()),
acceleration(Vec2::Zero()), rotation(0.0f), angularVelocity(0.0f),
size(1.0f), sizeDelta(0.0f), color(Colors::White),
colorDelta(Colors::Transparent), life(0.0f), maxLife(1.0f),
active(false) {}
};
// ============================================================================
// 发射器配置
// ============================================================================
struct EmitterConfig {
// 发射速率
float emissionRate = 100.0f; // 每秒发射粒子数
float emissionDuration = -1.0f; // 发射持续时间(-1表示无限
// 粒子生命周期
float minLife = 1.0f;
float maxLife = 2.0f;
// 粒子大小
float minStartSize = 10.0f;
float maxStartSize = 20.0f;
float minEndSize = 0.0f;
float maxEndSize = 5.0f;
// 粒子速度
Vec2 minVelocity = Vec2(-50.0f, -50.0f);
Vec2 maxVelocity = Vec2(50.0f, 50.0f);
// 粒子加速度
Vec2 acceleration = Vec2(0.0f, -100.0f); // 重力
// 粒子旋转
float minRotation = 0.0f;
float maxRotation = 360.0f;
float minAngularVelocity = -90.0f;
float maxAngularVelocity = 90.0f;
// 颜色
Color startColor = Colors::White;
Color endColor = Colors::Transparent;
// 发射形状
enum class Shape {
Point, // 点发射
Circle, // 圆形区域
Rectangle, // 矩形区域
Cone // 锥形
};
Shape shape = Shape::Point;
float shapeRadius = 50.0f; // 圆形/锥形半径
Vec2 shapeSize = Vec2(100.0f, 100.0f); // 矩形大小
float coneAngle = 45.0f; // 锥形角度
// 纹理
Ptr<Texture> texture = nullptr;
// 混合模式
BlendMode blendMode = BlendMode::Additive;
};
// ============================================================================
// 粒子发射器
// ============================================================================
class ParticleEmitter {
public:
ParticleEmitter();
~ParticleEmitter() = default;
// 形状生成函数(公有,用于查找表)
Vec2 randomPointShape();
Vec2 randomCircleShape();
Vec2 randomRectangleShape();
Vec2 randomConeShape();
// ------------------------------------------------------------------------
// 初始化和关闭
// ------------------------------------------------------------------------
bool init(size_t maxParticles);
void shutdown();
// ------------------------------------------------------------------------
// 配置
// ------------------------------------------------------------------------
void setConfig(const EmitterConfig &config) { config_ = config; }
const EmitterConfig &getConfig() const { return config_; }
// 链式配置API
ParticleEmitter &withEmissionRate(float rate) {
config_.emissionRate = rate;
return *this;
}
ParticleEmitter &withLife(float minLife, float maxLife) {
config_.minLife = minLife;
config_.maxLife = maxLife;
return *this;
}
ParticleEmitter &withSize(float minStart, float maxStart, float minEnd = 0.0f,
float maxEnd = 0.0f) {
config_.minStartSize = minStart;
config_.maxStartSize = maxStart;
config_.minEndSize = minEnd;
config_.maxEndSize = maxEnd;
return *this;
}
ParticleEmitter &withVelocity(const Vec2 &minVel, const Vec2 &maxVel) {
config_.minVelocity = minVel;
config_.maxVelocity = maxVel;
return *this;
}
ParticleEmitter &withAcceleration(const Vec2 &accel) {
config_.acceleration = accel;
return *this;
}
ParticleEmitter &withColor(const Color &start, const Color &end) {
config_.startColor = start;
config_.endColor = end;
return *this;
}
ParticleEmitter &withTexture(Ptr<Texture> texture) {
config_.texture = texture;
return *this;
}
ParticleEmitter &withBlendMode(BlendMode mode) {
config_.blendMode = mode;
return *this;
}
// ------------------------------------------------------------------------
// 发射控制
// ------------------------------------------------------------------------
void start();
void stop();
void burst(int count); // 爆发发射
void reset();
bool isEmitting() const { return emitting_; }
// ------------------------------------------------------------------------
// 更新和渲染
// ------------------------------------------------------------------------
void update(float dt);
void render(RenderBackend &renderer);
// ------------------------------------------------------------------------
// 状态查询
// ------------------------------------------------------------------------
size_t getActiveParticleCount() const { return activeCount_; }
size_t getMaxParticles() const { return particles_.size(); }
bool isActive() const { return activeCount_ > 0 || emitting_; }
// ------------------------------------------------------------------------
// 变换
// ------------------------------------------------------------------------
void setPosition(const Vec2 &pos) { position_ = pos; }
void setRotation(float rot) { rotation_ = rot; }
Vec2 getPosition() const { return position_; }
float getRotation() const { return rotation_; }
private:
EmitterConfig config_;
std::vector<Particle> particles_;
size_t activeCount_ = 0;
Vec2 position_ = Vec2::Zero();
float rotation_ = 0.0f;
bool emitting_ = false;
float emissionTimer_ = 0.0f;
float emissionTime_ = 0.0f;
FastRNG rng_; // 使用快速 RNG 替代 std::mt19937
void emitParticle();
float randomFloat(float min, float max);
Vec2 randomPointInShape();
Vec2 randomVelocity();
};
// ============================================================================
// 粒子系统 - 管理多个发射器
// ============================================================================
class ParticleSystem : public Node {
public:
ParticleSystem();
~ParticleSystem() override = default;
// ------------------------------------------------------------------------
// 静态创建方法
// ------------------------------------------------------------------------
static Ptr<ParticleSystem> create();
// ------------------------------------------------------------------------
// 发射器管理
// ------------------------------------------------------------------------
Ptr<ParticleEmitter> addEmitter(const EmitterConfig &config = {});
void removeEmitter(Ptr<ParticleEmitter> emitter);
void removeAllEmitters();
size_t getEmitterCount() const { return emitters_.size(); }
// ------------------------------------------------------------------------
// 全局控制
// ------------------------------------------------------------------------
void startAll();
void stopAll();
void resetAll();
// ------------------------------------------------------------------------
// 预设
// ------------------------------------------------------------------------
static EmitterConfig PresetFire();
static EmitterConfig PresetSmoke();
static EmitterConfig PresetExplosion();
static EmitterConfig PresetSparkle();
static EmitterConfig PresetRain();
static EmitterConfig PresetSnow();
// ------------------------------------------------------------------------
// 重写Node方法
// ------------------------------------------------------------------------
void onUpdate(float dt) override;
void onDraw(RenderBackend &renderer) override;
private:
std::vector<Ptr<ParticleEmitter>> emitters_;
};
// ============================================================================
// 粒子预设(便捷类)
// ============================================================================
class ParticlePreset {
public:
static EmitterConfig Fire();
static EmitterConfig Smoke();
static EmitterConfig Explosion();
static EmitterConfig Sparkle();
static EmitterConfig Rain();
static EmitterConfig Snow();
static EmitterConfig Magic();
static EmitterConfig Bubbles();
};
} // namespace extra2d

View File

@ -1,228 +0,0 @@
#pragma once
#include <core/color.h>
#include <core/types.h>
#include <graphics/opengl/gl_shader.h>
#include <graphics/texture.h>
#include <functional>
#include <string>
#include <vector>
namespace extra2d {
// ============================================================================
// 前向声明
// ============================================================================
class RenderTarget;
class RenderBackend;
// ============================================================================
// 后处理效果基类
// ============================================================================
class PostProcessEffect {
public:
PostProcessEffect(const std::string &name);
virtual ~PostProcessEffect() = default;
// ------------------------------------------------------------------------
// 生命周期
// ------------------------------------------------------------------------
/**
* @brief
*/
virtual bool init();
/**
* @brief
*/
virtual void shutdown();
// ------------------------------------------------------------------------
// 渲染
// ------------------------------------------------------------------------
/**
* @brief
* @param source
* @param target
* @param renderer
*/
virtual void apply(const Texture &source, RenderTarget &target,
RenderBackend &renderer);
/**
* @brief Shader参数
*/
virtual void onShaderBind(GLShader &shader) {}
// ------------------------------------------------------------------------
// 状态
// ------------------------------------------------------------------------
const std::string &getName() const { return name_; }
bool isEnabled() const { return enabled_; }
void setEnabled(bool enabled) { enabled_ = enabled; }
bool isValid() const { return valid_; }
// ------------------------------------------------------------------------
// 链式API
// ------------------------------------------------------------------------
PostProcessEffect &withEnabled(bool enabled) {
enabled_ = enabled;
return *this;
}
protected:
std::string name_;
bool enabled_ = true;
bool valid_ = false;
Ptr<GLShader> shader_;
/**
* @brief Shader
*/
bool loadShader(const std::string &vertSource, const std::string &fragSource);
bool loadShaderFromFile(const std::string &vertPath,
const std::string &fragPath);
/**
* @brief
*/
void renderFullscreenQuad();
private:
static GLuint quadVao_;
static GLuint quadVbo_;
static bool quadInitialized_;
void initQuad();
void destroyQuad();
};
// ============================================================================
// 后处理栈 - 管理多个后处理效果
// ============================================================================
class PostProcessStack {
public:
PostProcessStack();
~PostProcessStack();
// ------------------------------------------------------------------------
// 初始化和关闭
// ------------------------------------------------------------------------
bool init(int width, int height);
void shutdown();
// ------------------------------------------------------------------------
// 效果管理
// ------------------------------------------------------------------------
/**
* @brief
*/
void addEffect(Ptr<PostProcessEffect> effect);
/**
* @brief
*/
void insertEffect(size_t index, Ptr<PostProcessEffect> effect);
/**
* @brief
*/
void removeEffect(const std::string &name);
void removeEffect(size_t index);
/**
* @brief
*/
Ptr<PostProcessEffect> getEffect(const std::string &name);
Ptr<PostProcessEffect> getEffect(size_t index);
/**
* @brief
*/
void clearEffects();
/**
* @brief
*/
size_t getEffectCount() const { return effects_.size(); }
// ------------------------------------------------------------------------
// 渲染
// ------------------------------------------------------------------------
/**
* @brief
*/
void beginCapture();
/**
* @brief
*/
void endCapture(RenderBackend &renderer);
/**
* @brief
*/
void process(const Texture &source, RenderTarget &target,
RenderBackend &renderer);
// ------------------------------------------------------------------------
// 配置
// ------------------------------------------------------------------------
void resize(int width, int height);
bool isValid() const { return valid_; }
// ------------------------------------------------------------------------
// 便捷方法 - 添加内置效果
// ------------------------------------------------------------------------
PostProcessStack &addBloom(float intensity = 1.0f, float threshold = 0.8f);
PostProcessStack &addBlur(float radius = 2.0f);
PostProcessStack &addColorGrading(const Color &tint);
PostProcessStack &addVignette(float intensity = 0.5f);
PostProcessStack &addChromaticAberration(float amount = 1.0f);
private:
std::vector<Ptr<PostProcessEffect>> effects_;
Ptr<RenderTarget> renderTargetA_;
Ptr<RenderTarget> renderTargetB_;
int width_ = 0;
int height_ = 0;
bool valid_ = false;
bool capturing_ = false;
};
// ============================================================================
// 全局后处理管理
// ============================================================================
class PostProcessManager {
public:
static PostProcessManager &getInstance();
void init(int width, int height);
void shutdown();
PostProcessStack &getMainStack() { return mainStack_; }
void resize(int width, int height);
void beginFrame();
void endFrame(RenderBackend &renderer);
private:
PostProcessManager() = default;
~PostProcessManager() = default;
PostProcessManager(const PostProcessManager &) = delete;
PostProcessManager &operator=(const PostProcessManager &) = delete;
PostProcessStack mainStack_;
bool initialized_ = false;
};
// ============================================================================
// 便捷宏
// ============================================================================
#define E2D_POST_PROCESS() ::extra2d::PostProcessManager::getInstance()
} // namespace extra2d

View File

@ -101,11 +101,6 @@
#include <spatial/spatial_hash.h>
#include <spatial/spatial_manager.h>
// Effects
#include <effects/post_process.h>
#include <effects/particle_system.h>
#include <effects/custom_effect_manager.h>
// Application
#include <app/application.h>

View File

@ -1,918 +0,0 @@
#include <effects/custom_effect_manager.h>
#include <utils/logger.h>
#include <fstream>
#include <nlohmann/json.hpp>
#include <sstream>
namespace extra2d {
using json = nlohmann::json;
// ============================================================================
// JSON序列化辅助函数
// ============================================================================
/**
* @brief Vec2转换为JSON数组
*/
static json vec2ToJson(const Vec2 &v) { return json::array({v.x, v.y}); }
/**
* @brief JSON数组解析Vec2
*/
static Vec2 jsonToVec2(const json &j) {
if (j.is_array() && j.size() >= 2) {
return Vec2(j[0].get<float>(), j[1].get<float>());
}
return Vec2();
}
/**
* @brief Color转换为JSON数组
*/
static json colorToJson(const Color &c) {
return json::array({c.r, c.g, c.b, c.a});
}
/**
* @brief JSON数组解析Color
*/
static Color jsonToColor(const json &j) {
if (j.is_array() && j.size() >= 4) {
return Color(j[0].get<float>(), j[1].get<float>(), j[2].get<float>(),
j[3].get<float>());
}
return Colors::White;
}
/**
* @brief EmitterConfig转换为JSON
*/
static json emitterConfigToJson(const EmitterConfig &config) {
json j;
j["emissionRate"] = config.emissionRate;
j["life"] = json::array({config.minLife, config.maxLife});
j["startSize"] = json::array({config.minStartSize, config.maxStartSize});
j["endSize"] = json::array({config.minEndSize, config.maxEndSize});
j["velocity"] = json::object({{"min", vec2ToJson(config.minVelocity)},
{"max", vec2ToJson(config.maxVelocity)}});
j["acceleration"] = vec2ToJson(config.acceleration);
j["startColor"] = colorToJson(config.startColor);
j["endColor"] = colorToJson(config.endColor);
j["blendMode"] = static_cast<int>(config.blendMode);
j["shape"] = static_cast<int>(config.shape);
j["shapeRadius"] = config.shapeRadius;
return j;
}
/**
* @brief JSON解析EmitterConfig
*/
static EmitterConfig jsonToEmitterConfig(const json &j) {
EmitterConfig config;
if (j.contains("emissionRate")) {
config.emissionRate = j["emissionRate"].get<float>();
}
if (j.contains("life") && j["life"].is_array() && j["life"].size() >= 2) {
config.minLife = j["life"][0].get<float>();
config.maxLife = j["life"][1].get<float>();
}
if (j.contains("startSize") && j["startSize"].is_array() &&
j["startSize"].size() >= 2) {
config.minStartSize = j["startSize"][0].get<float>();
config.maxStartSize = j["startSize"][1].get<float>();
}
if (j.contains("endSize") && j["endSize"].is_array() &&
j["endSize"].size() >= 2) {
config.minEndSize = j["endSize"][0].get<float>();
config.maxEndSize = j["endSize"][1].get<float>();
}
if (j.contains("velocity")) {
const auto &vel = j["velocity"];
if (vel.contains("min")) {
config.minVelocity = jsonToVec2(vel["min"]);
}
if (vel.contains("max")) {
config.maxVelocity = jsonToVec2(vel["max"]);
}
}
if (j.contains("acceleration")) {
config.acceleration = jsonToVec2(j["acceleration"]);
}
if (j.contains("startColor")) {
config.startColor = jsonToColor(j["startColor"]);
}
if (j.contains("endColor")) {
config.endColor = jsonToColor(j["endColor"]);
}
if (j.contains("blendMode")) {
config.blendMode = static_cast<BlendMode>(j["blendMode"].get<int>());
}
if (j.contains("shape")) {
config.shape = static_cast<EmitterConfig::Shape>(j["shape"].get<int>());
}
if (j.contains("shapeRadius")) {
config.shapeRadius = j["shapeRadius"].get<float>();
}
return config;
}
/**
* @brief CustomEffectConfig转换为JSON
*/
static json effectConfigToJson(const CustomEffectConfig &config) {
json j;
j["name"] = config.name;
j["type"] = static_cast<int>(config.type);
j["description"] = config.description;
j["duration"] = config.duration;
j["loop"] = config.loop;
j["delay"] = config.delay;
if (config.type == CustomEffectType::Particle) {
j["emitter"] = emitterConfigToJson(config.emitterConfig);
} else if (config.type == CustomEffectType::PostProcess) {
j["shaderVert"] = config.shaderVertPath;
j["shaderFrag"] = config.shaderFragPath;
j["params"] = config.shaderParams;
}
return j;
}
/**
* @brief JSON解析CustomEffectConfig
*/
static CustomEffectConfig jsonToEffectConfig(const json &j) {
CustomEffectConfig config;
if (j.contains("name")) {
config.name = j["name"].get<std::string>();
}
if (j.contains("type")) {
config.type = static_cast<CustomEffectType>(j["type"].get<int>());
}
if (j.contains("description")) {
config.description = j["description"].get<std::string>();
}
if (j.contains("duration")) {
config.duration = j["duration"].get<float>();
}
if (j.contains("loop")) {
config.loop = j["loop"].get<bool>();
}
if (j.contains("delay")) {
config.delay = j["delay"].get<float>();
}
if (j.contains("emitter")) {
config.emitterConfig = jsonToEmitterConfig(j["emitter"]);
}
if (j.contains("shaderVert")) {
config.shaderVertPath = j["shaderVert"].get<std::string>();
}
if (j.contains("shaderFrag")) {
config.shaderFragPath = j["shaderFrag"].get<std::string>();
}
if (j.contains("params")) {
for (auto &[key, value] : j["params"].items()) {
config.shaderParams[key] = value.get<float>();
}
}
return config;
}
// ============================================================================
// CustomEffect实现
// ============================================================================
CustomEffect::CustomEffect(const CustomEffectConfig &config)
: config_(config) {}
bool CustomEffect::init() { return true; }
void CustomEffect::update(float dt) {
if (!playing_ || paused_ || finished_)
return;
// 处理延迟
if (delayTimer_ < config_.delay) {
delayTimer_ += dt;
return;
}
elapsedTime_ += dt;
// 检查是否结束
if (config_.duration > 0 && elapsedTime_ >= config_.duration) {
if (config_.loop) {
elapsedTime_ = 0.0f;
} else {
finished_ = true;
playing_ = false;
}
}
}
void CustomEffect::render(RenderBackend &renderer) {
// 基类不渲染任何内容
}
void CustomEffect::shutdown() {
playing_ = false;
paused_ = false;
finished_ = true;
}
void CustomEffect::play() {
if (finished_) {
reset();
}
playing_ = true;
paused_ = false;
}
void CustomEffect::pause() { paused_ = true; }
void CustomEffect::stop() {
playing_ = false;
paused_ = false;
}
void CustomEffect::reset() {
elapsedTime_ = 0.0f;
delayTimer_ = 0.0f;
finished_ = false;
playing_ = false;
paused_ = false;
}
// ============================================================================
// CustomParticleEffect实现
// ============================================================================
CustomParticleEffect::CustomParticleEffect(const CustomEffectConfig &config)
: CustomEffect(config) {}
bool CustomParticleEffect::init() {
particleSystem_ = ParticleSystem::create();
if (!particleSystem_) {
E2D_ERROR("创建粒子系统失败");
return false;
}
emitter_ = particleSystem_->addEmitter(config_.emitterConfig);
if (!emitter_) {
E2D_ERROR("创建粒子发射器失败");
return false;
}
// 初始化时启动发射器
emitter_->start();
return true;
}
void CustomParticleEffect::play() {
CustomEffect::play();
if (emitter_) {
emitter_->start();
}
}
void CustomParticleEffect::stop() {
CustomEffect::stop();
if (emitter_) {
emitter_->stop();
}
}
void CustomParticleEffect::update(float dt) {
CustomEffect::update(dt);
if (particleSystem_) {
particleSystem_->setPosition(position_);
particleSystem_->onUpdate(dt);
}
}
void CustomParticleEffect::render(RenderBackend &renderer) {
if (particleSystem_) {
particleSystem_->onDraw(renderer);
}
}
void CustomParticleEffect::shutdown() {
if (emitter_) {
emitter_->stop();
emitter_.reset();
}
if (particleSystem_) {
particleSystem_->removeAllEmitters();
particleSystem_.reset();
}
CustomEffect::shutdown();
}
// ============================================================================
// CustomPostProcessEffect实现
// ============================================================================
CustomPostProcessEffect::CustomPostProcessEffect(
const CustomEffectConfig &config)
: CustomEffect(config), PostProcessEffect(config.name) {}
bool CustomPostProcessEffect::init() {
if (!config_.shaderVertPath.empty() && !config_.shaderFragPath.empty()) {
if (!loadShaderFromFile(config_.shaderVertPath, config_.shaderFragPath)) {
E2D_ERROR("加载后处理Shader失败");
return false;
}
}
runtimeParams_ = config_.shaderParams;
return true;
}
void CustomPostProcessEffect::update(float dt) { CustomEffect::update(dt); }
void CustomPostProcessEffect::shutdown() {
PostProcessEffect::shutdown();
CustomEffect::shutdown();
}
void CustomPostProcessEffect::onShaderBind(GLShader &shader) {
for (const auto &[name, value] : runtimeParams_) {
shader.setFloat(name, value);
}
}
void CustomPostProcessEffect::setParam(const std::string &name, float value) {
runtimeParams_[name] = value;
}
float CustomPostProcessEffect::getParam(const std::string &name) const {
auto it = runtimeParams_.find(name);
if (it != runtimeParams_.end()) {
return it->second;
}
return 0.0f;
}
// ============================================================================
// CustomEffectFactory实现
// ============================================================================
CustomEffectFactory &CustomEffectFactory::getInstance() {
static CustomEffectFactory instance;
return instance;
}
void CustomEffectFactory::registerEffect(const std::string &typeName,
EffectCreator creator) {
creators_[typeName] = creator;
E2D_INFO("注册自定义特效类型: {}", typeName);
}
Ptr<CustomEffect>
CustomEffectFactory::create(const std::string &typeName,
const CustomEffectConfig &config) {
auto it = creators_.find(typeName);
if (it != creators_.end()) {
return it->second(config);
}
// 默认创建器
if (typeName == "Particle") {
return std::make_shared<CustomParticleEffect>(config);
} else if (typeName == "PostProcess") {
return std::make_shared<CustomPostProcessEffect>(config);
}
E2D_ERROR("未知的特效类型: {}", typeName);
return nullptr;
}
bool CustomEffectFactory::isRegistered(const std::string &typeName) const {
return creators_.find(typeName) != creators_.end();
}
std::vector<std::string> CustomEffectFactory::getRegisteredTypes() const {
std::vector<std::string> types;
for (const auto &[name, _] : creators_) {
types.push_back(name);
}
return types;
}
// ============================================================================
// CustomEffectManager实现
// ============================================================================
CustomEffectManager &CustomEffectManager::getInstance() {
static CustomEffectManager instance;
return instance;
}
bool CustomEffectManager::init() {
E2D_INFO("初始化自定义特效管理器...");
// 注册默认特效类型
auto &factory = E2D_CUSTOM_EFFECT_FACTORY();
factory.registerEffect("Particle", [](const CustomEffectConfig &config) {
return std::make_shared<CustomParticleEffect>(config);
});
factory.registerEffect("PostProcess", [](const CustomEffectConfig &config) {
return std::make_shared<CustomPostProcessEffect>(config);
});
E2D_INFO("自定义特效管理器初始化完成");
return true;
}
void CustomEffectManager::shutdown() {
E2D_INFO("关闭自定义特效管理器...");
stopAll();
activeEffects_.clear();
configs_.clear();
}
bool CustomEffectManager::loadFromFile(const std::string &filepath) {
std::ifstream file(filepath);
if (!file.is_open()) {
E2D_ERROR("无法打开特效配置文件: {}", filepath);
return false;
}
try {
// 尝试解析为JSON
json j;
file >> j;
file.close();
if (j.is_array()) {
// 多个特效配置数组
for (const auto &effectJson : j) {
auto config = jsonToEffectConfig(effectJson);
if (!config.name.empty()) {
registerConfig(config.name, config);
}
}
} else if (j.is_object()) {
// 单个特效配置
auto config = jsonToEffectConfig(j);
if (!config.name.empty()) {
registerConfig(config.name, config);
}
}
E2D_INFO("从JSON文件加载特效配置: {}", filepath);
return true;
} catch (const json::exception &e) {
// JSON解析失败回退到文本格式
file.close();
return loadFromTextFile(filepath);
}
}
bool CustomEffectManager::loadFromTextFile(const std::string &filepath) {
std::ifstream file(filepath);
if (!file.is_open()) {
E2D_ERROR("无法打开特效配置文件: {}", filepath);
return false;
}
// 简化格式:每行一个特效配置
// 格式: EFFECT name type
// PARAM key value
// END
std::string line;
CustomEffectConfig currentConfig;
bool inEffect = false;
while (std::getline(file, line)) {
// 跳过空行和注释
if (line.empty() || line[0] == '#')
continue;
std::istringstream iss(line);
std::string cmd;
iss >> cmd;
if (cmd == "EFFECT") {
// 开始新特效
if (inEffect) {
// 保存上一个特效
registerConfig(currentConfig.name, currentConfig);
}
inEffect = true;
currentConfig = CustomEffectConfig();
std::string type;
iss >> currentConfig.name >> type;
if (type == "Particle") {
currentConfig.type = CustomEffectType::Particle;
} else if (type == "PostProcess") {
currentConfig.type = CustomEffectType::PostProcess;
} else {
currentConfig.type = CustomEffectType::Particle;
}
} else if (cmd == "DESC") {
std::getline(iss, currentConfig.description);
// 去除前导空格
if (!currentConfig.description.empty() &&
currentConfig.description[0] == ' ') {
currentConfig.description = currentConfig.description.substr(1);
}
} else if (cmd == "DURATION") {
iss >> currentConfig.duration;
} else if (cmd == "LOOP") {
std::string val;
iss >> val;
currentConfig.loop = (val == "true" || val == "1");
} else if (cmd == "EMISSION") {
iss >> currentConfig.emitterConfig.emissionRate;
} else if (cmd == "LIFE") {
iss >> currentConfig.emitterConfig.minLife >>
currentConfig.emitterConfig.maxLife;
} else if (cmd == "SIZE_START") {
iss >> currentConfig.emitterConfig.minStartSize >>
currentConfig.emitterConfig.maxStartSize;
} else if (cmd == "SIZE_END") {
iss >> currentConfig.emitterConfig.minEndSize >>
currentConfig.emitterConfig.maxEndSize;
} else if (cmd == "VELOCITY") {
iss >> currentConfig.emitterConfig.minVelocity.x >>
currentConfig.emitterConfig.minVelocity.y >>
currentConfig.emitterConfig.maxVelocity.x >>
currentConfig.emitterConfig.maxVelocity.y;
} else if (cmd == "ACCEL") {
iss >> currentConfig.emitterConfig.acceleration.x >>
currentConfig.emitterConfig.acceleration.y;
} else if (cmd == "COLOR_START") {
iss >> currentConfig.emitterConfig.startColor.r >>
currentConfig.emitterConfig.startColor.g >>
currentConfig.emitterConfig.startColor.b >>
currentConfig.emitterConfig.startColor.a;
} else if (cmd == "COLOR_END") {
iss >> currentConfig.emitterConfig.endColor.r >>
currentConfig.emitterConfig.endColor.g >>
currentConfig.emitterConfig.endColor.b >>
currentConfig.emitterConfig.endColor.a;
} else if (cmd == "BLEND") {
std::string mode;
iss >> mode;
if (mode == "Additive") {
currentConfig.emitterConfig.blendMode = BlendMode::Additive;
} else if (mode == "Alpha") {
currentConfig.emitterConfig.blendMode = BlendMode::Alpha;
} else {
currentConfig.emitterConfig.blendMode = BlendMode::None;
}
} else if (cmd == "END") {
// 结束当前特效
if (inEffect) {
registerConfig(currentConfig.name, currentConfig);
inEffect = false;
}
}
}
// 保存最后一个特效
if (inEffect) {
registerConfig(currentConfig.name, currentConfig);
}
file.close();
E2D_INFO("从文本文件加载特效配置: {}", filepath);
return true;
}
bool CustomEffectManager::saveToFile(const std::string &name,
const std::string &filepath,
bool useJson) {
auto it = configs_.find(name);
if (it == configs_.end()) {
E2D_ERROR("特效配置不存在: {}", name);
return false;
}
std::ofstream file(filepath);
if (!file.is_open()) {
E2D_ERROR("无法创建文件: {}", filepath);
return false;
}
if (useJson) {
// 保存为JSON格式
json j = effectConfigToJson(it->second);
file << j.dump(2); // 缩进2个空格更易读
E2D_INFO("保存特效配置到JSON文件: {}", filepath);
} else {
// 保存为文本格式
const auto &config = it->second;
file << "# Easy2D Custom Effect Config\n";
file << "# Generated automatically\n\n";
file << "EFFECT " << config.name << " ";
if (config.type == CustomEffectType::Particle) {
file << "Particle\n";
} else if (config.type == CustomEffectType::PostProcess) {
file << "PostProcess\n";
} else {
file << "Particle\n";
}
file << "DESC " << config.description << "\n";
file << "DURATION " << config.duration << "\n";
file << "LOOP " << (config.loop ? "true" : "false") << "\n";
if (config.type == CustomEffectType::Particle) {
const auto &ec = config.emitterConfig;
file << "EMISSION " << ec.emissionRate << "\n";
file << "LIFE " << ec.minLife << " " << ec.maxLife << "\n";
file << "SIZE_START " << ec.minStartSize << " " << ec.maxStartSize
<< "\n";
file << "SIZE_END " << ec.minEndSize << " " << ec.maxEndSize << "\n";
file << "VELOCITY " << ec.minVelocity.x << " " << ec.minVelocity.y << " "
<< ec.maxVelocity.x << " " << ec.maxVelocity.y << "\n";
file << "ACCEL " << ec.acceleration.x << " " << ec.acceleration.y << "\n";
file << "COLOR_START " << ec.startColor.r << " " << ec.startColor.g << " "
<< ec.startColor.b << " " << ec.startColor.a << "\n";
file << "COLOR_END " << ec.endColor.r << " " << ec.endColor.g << " "
<< ec.endColor.b << " " << ec.endColor.a << "\n";
file << "BLEND ";
switch (ec.blendMode) {
case BlendMode::Additive:
file << "Additive\n";
break;
case BlendMode::Alpha:
file << "Alpha\n";
break;
default:
file << "None\n";
break;
}
}
file << "END\n";
E2D_INFO("保存特效配置到文本文件: {}", filepath);
}
file.close();
return true;
}
bool CustomEffectManager::saveAllToFile(const std::string &filepath) {
std::ofstream file(filepath);
if (!file.is_open()) {
E2D_ERROR("无法创建文件: {}", filepath);
return false;
}
json effectsArray = json::array();
for (const auto &[name, config] : configs_) {
effectsArray.push_back(effectConfigToJson(config));
}
file << effectsArray.dump(2);
file.close();
E2D_INFO("保存所有特效配置到: {}", filepath);
return true;
}
void CustomEffectManager::registerConfig(const std::string &name,
const CustomEffectConfig &config) {
configs_[name] = config;
E2D_INFO("注册特效配置: {}", name);
}
CustomEffectConfig *CustomEffectManager::getConfig(const std::string &name) {
auto it = configs_.find(name);
if (it != configs_.end()) {
return &it->second;
}
return nullptr;
}
void CustomEffectManager::removeConfig(const std::string &name) {
configs_.erase(name);
}
std::vector<std::string> CustomEffectManager::getConfigNames() const {
std::vector<std::string> names;
for (const auto &[name, _] : configs_) {
names.push_back(name);
}
return names;
}
Ptr<CustomEffect> CustomEffectManager::createEffect(const std::string &name) {
auto config = getConfig(name);
if (!config) {
E2D_ERROR("特效配置不存在: {}", name);
return nullptr;
}
return createEffectFromConfig(*config);
}
Ptr<CustomEffect>
CustomEffectManager::createEffectFromConfig(const CustomEffectConfig &config) {
std::string typeName;
switch (config.type) {
case CustomEffectType::Particle:
typeName = "Particle";
break;
case CustomEffectType::PostProcess:
typeName = "PostProcess";
break;
default:
typeName = "Particle";
break;
}
auto effect = E2D_CUSTOM_EFFECT_FACTORY().create(typeName, config);
if (effect && effect->init()) {
activeEffects_.push_back(effect);
return effect;
}
return nullptr;
}
void CustomEffectManager::destroyEffect(Ptr<CustomEffect> effect) {
if (!effect)
return;
effect->shutdown();
auto it = std::find(activeEffects_.begin(), activeEffects_.end(), effect);
if (it != activeEffects_.end()) {
activeEffects_.erase(it);
}
}
void CustomEffectManager::update(float dt) {
for (auto &effect : activeEffects_) {
if (effect->isPlaying()) {
effect->update(dt);
}
}
// 清理已完成的特效
activeEffects_.erase(std::remove_if(activeEffects_.begin(),
activeEffects_.end(),
[](const Ptr<CustomEffect> &effect) {
return effect->isFinished();
}),
activeEffects_.end());
}
void CustomEffectManager::render(RenderBackend &renderer) {
for (auto &effect : activeEffects_) {
if (effect->isPlaying()) {
effect->render(renderer);
}
}
}
void CustomEffectManager::stopAll() {
for (auto &effect : activeEffects_) {
effect->stop();
}
}
Ptr<CustomEffect> CustomEffectManager::play(const std::string &name,
const Vec2 &position) {
auto effect = createEffect(name);
if (effect) {
effect->setPosition(position);
effect->play();
}
return effect;
}
void CustomEffectManager::playOneShot(const std::string &name,
const Vec2 &position) {
auto effect = play(name, position);
if (effect) {
// 设置非循环,播放一次后自动销毁
effect->play();
}
}
// ============================================================================
// EffectBuilder实现
// ============================================================================
CustomEffectConfig EffectBuilder::Particle(const std::string &name) {
CustomEffectConfig config;
config.name = name;
config.type = CustomEffectType::Particle;
config.duration = -1.0f;
config.loop = true;
config.delay = 0.0f;
// 默认粒子配置
config.emitterConfig.emissionRate = 100.0f;
config.emitterConfig.minLife = 1.0f;
config.emitterConfig.maxLife = 2.0f;
config.emitterConfig.minStartSize = 10.0f;
config.emitterConfig.maxStartSize = 20.0f;
config.emitterConfig.minEndSize = 0.0f;
config.emitterConfig.maxEndSize = 5.0f;
config.emitterConfig.minVelocity = Vec2(-50.0f, -50.0f);
config.emitterConfig.maxVelocity = Vec2(50.0f, 50.0f);
config.emitterConfig.acceleration = Vec2(0.0f, 0.0f);
config.emitterConfig.startColor = Colors::White;
config.emitterConfig.endColor = Colors::Transparent;
config.emitterConfig.blendMode = BlendMode::Additive;
return config;
}
CustomEffectConfig EffectBuilder::Fire(const std::string &name) {
CustomEffectConfig config = Particle(name);
config.emitterConfig = ParticlePreset::Fire();
return config;
}
CustomEffectConfig EffectBuilder::Smoke(const std::string &name) {
CustomEffectConfig config = Particle(name);
config.emitterConfig = ParticlePreset::Smoke();
return config;
}
CustomEffectConfig EffectBuilder::Explosion(const std::string &name) {
CustomEffectConfig config = Particle(name);
config.emitterConfig = ParticlePreset::Explosion();
config.duration = 2.0f;
config.loop = false;
return config;
}
CustomEffectConfig EffectBuilder::Magic(const std::string &name) {
CustomEffectConfig config = Particle(name);
config.emitterConfig = ParticlePreset::Magic();
return config;
}
CustomEffectConfig EffectBuilder::Sparkle(const std::string &name) {
CustomEffectConfig config = Particle(name);
config.emitterConfig = ParticlePreset::Sparkle();
return config;
}
CustomEffectConfig EffectBuilder::Bloom(const std::string &name) {
CustomEffectConfig config;
config.name = name;
config.type = CustomEffectType::PostProcess;
config.duration = -1.0f;
config.loop = true;
config.shaderParams["intensity"] = 1.5f;
config.shaderParams["threshold"] = 0.8f;
return config;
}
CustomEffectConfig EffectBuilder::Blur(const std::string &name) {
CustomEffectConfig config;
config.name = name;
config.type = CustomEffectType::PostProcess;
config.duration = -1.0f;
config.loop = true;
config.shaderParams["radius"] = 2.0f;
return config;
}
CustomEffectConfig EffectBuilder::Vignette(const std::string &name) {
CustomEffectConfig config;
config.name = name;
config.type = CustomEffectType::PostProcess;
config.duration = -1.0f;
config.loop = true;
config.shaderParams["intensity"] = 0.5f;
return config;
}
CustomEffectConfig EffectBuilder::ColorGrading(const std::string &name) {
CustomEffectConfig config;
config.name = name;
config.type = CustomEffectType::PostProcess;
config.duration = -1.0f;
config.loop = true;
config.shaderParams["brightness"] = 1.0f;
config.shaderParams["contrast"] = 1.0f;
config.shaderParams["saturation"] = 1.0f;
return config;
}
} // namespace extra2d

View File

@ -1,468 +0,0 @@
#include <cmath>
#include <effects/particle_system.h>
#include <graphics/render_backend.h>
#include <utils/logger.h>
namespace extra2d {
// ============================================================================
// 形状生成函数类型和查找表
// ============================================================================
using ShapeGenerator = Vec2 (ParticleEmitter::*)();
// 形状生成函数指针数组,索引对应 EmitterConfig::Shape 枚举值
static constexpr ShapeGenerator SHAPE_GENERATORS[] = {
&ParticleEmitter::randomPointShape, // Shape::Point = 0
&ParticleEmitter::randomCircleShape, // Shape::Circle = 1
&ParticleEmitter::randomRectangleShape, // Shape::Rectangle = 2
&ParticleEmitter::randomConeShape // Shape::Cone = 3
};
static constexpr size_t SHAPE_GENERATOR_COUNT = sizeof(SHAPE_GENERATORS) / sizeof(SHAPE_GENERATORS[0]);
// ============================================================================
// ParticleEmitter实现
// ============================================================================
ParticleEmitter::ParticleEmitter() : rng_(static_cast<uint32_t>(std::random_device{}())) {}
bool ParticleEmitter::init(size_t maxParticles) {
particles_.resize(maxParticles);
activeCount_ = 0;
return true;
}
void ParticleEmitter::shutdown() {
particles_.clear();
activeCount_ = 0;
}
void ParticleEmitter::start() {
emitting_ = true;
emissionTime_ = 0.0f;
}
void ParticleEmitter::stop() { emitting_ = false; }
void ParticleEmitter::burst(int count) {
for (int i = 0; i < count && activeCount_ < particles_.size(); ++i) {
emitParticle();
}
}
void ParticleEmitter::reset() {
for (auto &particle : particles_) {
particle.active = false;
}
activeCount_ = 0;
emissionTimer_ = 0.0f;
emissionTime_ = 0.0f;
}
void ParticleEmitter::update(float dt) {
// 发射新粒子
if (emitting_) {
emissionTime_ += dt;
// 检查持续时间
if (config_.emissionDuration > 0 &&
emissionTime_ >= config_.emissionDuration) {
emitting_ = false;
}
emissionTimer_ += dt;
float emissionInterval = 1.0f / config_.emissionRate;
while (emissionTimer_ >= emissionInterval &&
activeCount_ < particles_.size()) {
emitParticle();
emissionTimer_ -= emissionInterval;
}
}
// 更新活跃粒子
size_t newActiveCount = 0;
for (size_t i = 0; i < activeCount_; ++i) {
auto &p = particles_[i];
if (!p.active)
continue;
// 更新生命周期
p.life -= dt;
if (p.life <= 0.0f) {
p.active = false;
continue;
}
// 更新物理
p.velocity += p.acceleration * dt;
p.position += p.velocity * dt;
p.rotation += p.angularVelocity * dt;
// 更新大小
p.size += p.sizeDelta * dt;
if (p.size < 0.0f)
p.size = 0.0f;
// 更新颜色
p.color += p.colorDelta * dt;
// 保持活跃
if (newActiveCount != i) {
particles_[newActiveCount] = p;
}
newActiveCount++;
}
activeCount_ = newActiveCount;
}
void ParticleEmitter::render(RenderBackend &renderer) {
if (activeCount_ == 0)
return;
// 设置混合模式
renderer.setBlendMode(config_.blendMode);
// 渲染所有活跃粒子
if (config_.texture) {
// 使用纹理批量渲染
renderer.beginSpriteBatch();
for (size_t i = 0; i < activeCount_; ++i) {
const auto &p = particles_[i];
if (!p.active)
continue;
// 计算目标矩形
// 锚点由 RenderBackend 在绘制时处理,这里只传递位置和尺寸
Rect destRect(p.position.x, p.position.y, p.size, p.size);
renderer.drawSprite(
*config_.texture, destRect,
Rect(0, 0, config_.texture->getWidth(), config_.texture->getHeight()),
p.color, p.rotation, Vec2(0.5f, 0.5f));
}
renderer.endSpriteBatch();
} else {
// 没有纹理,使用圆形填充渲染
for (size_t i = 0; i < activeCount_; ++i) {
const auto &p = particles_[i];
if (!p.active)
continue;
// 渲染圆形粒子
renderer.fillCircle(p.position, p.size * 0.5f, p.color);
}
}
}
void ParticleEmitter::emitParticle() {
if (activeCount_ >= particles_.size())
return;
Particle &p = particles_[activeCount_];
p.active = true;
// 位置
p.position = position_ + randomPointInShape();
// 速度
p.velocity = randomVelocity();
// 加速度
p.acceleration = config_.acceleration;
// 旋转
p.rotation = randomFloat(config_.minRotation, config_.maxRotation);
p.angularVelocity =
randomFloat(config_.minAngularVelocity, config_.maxAngularVelocity);
// 大小
float startSize = randomFloat(config_.minStartSize, config_.maxStartSize);
float endSize = randomFloat(config_.minEndSize, config_.maxEndSize);
p.size = startSize;
// 生命周期
p.maxLife = randomFloat(config_.minLife, config_.maxLife);
p.life = p.maxLife;
// 计算每帧变化量
if (p.maxLife > 0.0f) {
p.sizeDelta = (endSize - startSize) / p.maxLife;
p.colorDelta = (config_.endColor - config_.startColor) / p.maxLife;
}
// 颜色
p.color = config_.startColor;
activeCount_++;
}
float ParticleEmitter::randomFloat(float min, float max) {
return rng_.nextFloat(min, max);
}
Vec2 ParticleEmitter::randomPointInShape() {
// 使用查找表替代 switch
size_t shapeIndex = static_cast<size_t>(config_.shape);
if (shapeIndex < SHAPE_GENERATOR_COUNT) {
return (this->*SHAPE_GENERATORS[shapeIndex])();
}
return Vec2::Zero();
}
Vec2 ParticleEmitter::randomPointShape() {
return Vec2::Zero();
}
Vec2 ParticleEmitter::randomCircleShape() {
float angle = randomFloat(0.0f, 2.0f * 3.14159265359f);
float radius = randomFloat(0.0f, config_.shapeRadius);
return Vec2(std::cos(angle) * radius, std::sin(angle) * radius);
}
Vec2 ParticleEmitter::randomRectangleShape() {
return Vec2(
randomFloat(-config_.shapeSize.x * 0.5f, config_.shapeSize.x * 0.5f),
randomFloat(-config_.shapeSize.y * 0.5f, config_.shapeSize.y * 0.5f));
}
Vec2 ParticleEmitter::randomConeShape() {
float angle =
randomFloat(-config_.coneAngle * 0.5f, config_.coneAngle * 0.5f);
float radius = randomFloat(0.0f, config_.shapeRadius);
float rad = angle * 3.14159265359f / 180.0f;
return Vec2(std::cos(rad) * radius, std::sin(rad) * radius);
}
Vec2 ParticleEmitter::randomVelocity() {
return Vec2(randomFloat(config_.minVelocity.x, config_.maxVelocity.x),
randomFloat(config_.minVelocity.y, config_.maxVelocity.y));
}
// ============================================================================
// ParticleSystem实现
// ============================================================================
ParticleSystem::ParticleSystem() {}
Ptr<ParticleSystem> ParticleSystem::create() {
return std::make_shared<ParticleSystem>();
}
Ptr<ParticleEmitter> ParticleSystem::addEmitter(const EmitterConfig &config) {
auto emitter = std::make_shared<ParticleEmitter>();
emitter->setConfig(config);
emitter->init(1000); // 默认最大1000个粒子
emitters_.push_back(emitter);
return emitter;
}
void ParticleSystem::removeEmitter(Ptr<ParticleEmitter> emitter) {
auto it = std::find(emitters_.begin(), emitters_.end(), emitter);
if (it != emitters_.end()) {
(*it)->shutdown();
emitters_.erase(it);
}
}
void ParticleSystem::removeAllEmitters() {
for (auto &emitter : emitters_) {
emitter->shutdown();
}
emitters_.clear();
}
void ParticleSystem::startAll() {
for (auto &emitter : emitters_) {
emitter->start();
}
}
void ParticleSystem::stopAll() {
for (auto &emitter : emitters_) {
emitter->stop();
}
}
void ParticleSystem::resetAll() {
for (auto &emitter : emitters_) {
emitter->reset();
}
}
void ParticleSystem::onUpdate(float dt) {
// 获取粒子系统的世界位置
auto worldPos = convertToWorldSpace(Vec2::Zero());
for (auto &emitter : emitters_) {
// 更新发射器位置为粒子系统的世界位置
emitter->setPosition(worldPos);
// 更新发射器
emitter->update(dt);
}
}
void ParticleSystem::onDraw(RenderBackend &renderer) {
for (auto &emitter : emitters_) {
emitter->render(renderer);
}
}
// ============================================================================
// 预设实现
// ============================================================================
EmitterConfig ParticlePreset::Fire() {
EmitterConfig config;
config.emissionRate = 200.0f;
config.minLife = 0.5f;
config.maxLife = 1.5f;
config.minStartSize = 20.0f;
config.maxStartSize = 40.0f;
config.minEndSize = 5.0f;
config.maxEndSize = 10.0f;
config.minVelocity = Vec2(-30.0f, -150.0f); // 向上负y
config.maxVelocity = Vec2(30.0f, -50.0f);
config.acceleration = Vec2(0.0f, 0.0f);
config.startColor = Color(1.0f, 0.8f, 0.2f, 1.0f); // 黄色
config.endColor = Color(1.0f, 0.2f, 0.0f, 0.0f); // 红色透明
config.blendMode = BlendMode::Additive;
return config;
}
EmitterConfig ParticlePreset::Smoke() {
EmitterConfig config;
config.emissionRate = 50.0f;
config.minLife = 2.0f;
config.maxLife = 4.0f;
config.minStartSize = 30.0f;
config.maxStartSize = 60.0f;
config.minEndSize = 80.0f;
config.maxEndSize = 120.0f;
config.minVelocity = Vec2(-20.0f, -60.0f); // 向上负y
config.maxVelocity = Vec2(20.0f, -30.0f);
config.acceleration = Vec2(0.0f, -10.0f); // 向上加速度
config.startColor = Color(0.5f, 0.5f, 0.5f, 0.5f); // 灰色半透明
config.endColor = Color(0.3f, 0.3f, 0.3f, 0.0f); // 深灰透明
config.blendMode = BlendMode::Alpha;
return config;
}
EmitterConfig ParticlePreset::Explosion() {
EmitterConfig config;
config.emissionRate = 1000.0f;
config.emissionDuration = 0.1f; // 瞬间爆发
config.minLife = 0.5f;
config.maxLife = 1.5f;
config.minStartSize = 10.0f;
config.maxStartSize = 30.0f;
config.minEndSize = 0.0f;
config.maxEndSize = 5.0f;
config.minVelocity = Vec2(-300.0f, -300.0f);
config.maxVelocity = Vec2(300.0f, 300.0f);
config.acceleration = Vec2(0.0f, -50.0f);
config.startColor = Color(1.0f, 1.0f, 0.5f, 1.0f); // 亮黄
config.endColor = Color(1.0f, 0.3f, 0.0f, 0.0f); // 橙红透明
config.blendMode = BlendMode::Additive;
return config;
}
EmitterConfig ParticlePreset::Sparkle() {
EmitterConfig config;
config.emissionRate = 20.0f;
config.minLife = 0.2f;
config.maxLife = 0.8f;
config.minStartSize = 2.0f;
config.maxStartSize = 5.0f;
config.minEndSize = 0.0f;
config.maxEndSize = 2.0f;
config.minVelocity = Vec2(-10.0f, -10.0f);
config.maxVelocity = Vec2(10.0f, 10.0f);
config.acceleration = Vec2(0.0f, 0.0f);
config.startColor = Color(1.0f, 1.0f, 1.0f, 1.0f); // 白色
config.endColor = Color(1.0f, 1.0f, 1.0f, 0.0f); // 透明
config.blendMode = BlendMode::Additive;
return config;
}
EmitterConfig ParticlePreset::Rain() {
EmitterConfig config;
config.emissionRate = 500.0f;
config.minLife = 1.0f;
config.maxLife = 2.0f;
config.minStartSize = 2.0f;
config.maxStartSize = 4.0f;
config.minEndSize = 2.0f;
config.maxEndSize = 4.0f;
config.minVelocity = Vec2(-100.0f, -400.0f);
config.maxVelocity = Vec2(100.0f, -600.0f);
config.acceleration = Vec2(0.0f, -100.0f);
config.startColor = Color(0.7f, 0.8f, 1.0f, 0.6f); // 淡蓝
config.endColor = Color(0.7f, 0.8f, 1.0f, 0.3f); // 淡蓝半透明
config.shape = EmitterConfig::Shape::Rectangle;
config.shapeSize = Vec2(800.0f, 100.0f); // 在顶部区域发射
config.blendMode = BlendMode::Alpha;
return config;
}
EmitterConfig ParticlePreset::Snow() {
EmitterConfig config;
config.emissionRate = 100.0f;
config.minLife = 3.0f;
config.maxLife = 6.0f;
config.minStartSize = 5.0f;
config.maxStartSize = 10.0f;
config.minEndSize = 5.0f;
config.maxEndSize = 10.0f;
config.minVelocity = Vec2(-30.0f, -30.0f);
config.maxVelocity = Vec2(30.0f, -80.0f);
config.acceleration = Vec2(0.0f, 0.0f);
config.startColor = Color(1.0f, 1.0f, 1.0f, 0.8f); // 白色
config.endColor = Color(1.0f, 1.0f, 1.0f, 0.8f); // 白色
config.shape = EmitterConfig::Shape::Rectangle;
config.shapeSize = Vec2(800.0f, 100.0f);
config.blendMode = BlendMode::Alpha;
return config;
}
EmitterConfig ParticlePreset::Magic() {
EmitterConfig config;
config.emissionRate = 100.0f;
config.minLife = 1.0f;
config.maxLife = 2.0f;
config.minStartSize = 5.0f;
config.maxStartSize = 15.0f;
config.minEndSize = 0.0f;
config.maxEndSize = 5.0f;
config.minVelocity = Vec2(-50.0f, -50.0f); // 主要向上
config.maxVelocity = Vec2(50.0f, -50.0f);
config.acceleration = Vec2(0.0f, -20.0f); // 向上加速度
config.startColor = Color(0.5f, 0.2f, 1.0f, 1.0f); // 紫色
config.endColor = Color(0.2f, 0.8f, 1.0f, 0.0f); // 青色透明
config.blendMode = BlendMode::Additive;
return config;
}
EmitterConfig ParticlePreset::Bubbles() {
EmitterConfig config;
config.emissionRate = 30.0f;
config.minLife = 2.0f;
config.maxLife = 4.0f;
config.minStartSize = 5.0f;
config.maxStartSize = 15.0f;
config.minEndSize = 5.0f;
config.maxEndSize = 15.0f;
config.minVelocity = Vec2(-20.0f, 20.0f);
config.maxVelocity = Vec2(20.0f, 60.0f);
config.acceleration = Vec2(0.0f, 30.0f);
config.startColor = Color(0.8f, 0.9f, 1.0f, 0.4f); // 淡蓝透明
config.endColor = Color(0.8f, 0.9f, 1.0f, 0.1f); // 更透明
config.blendMode = BlendMode::Alpha;
return config;
}
} // namespace extra2d

View File

@ -1,389 +0,0 @@
#include <effects/post_process.h>
#include <graphics/render_backend.h>
#include <graphics/render_target.h>
#include <utils/logger.h>
#include <glad/glad.h>
namespace extra2d {
// ============================================================================
// 静态成员初始化
// ============================================================================
GLuint PostProcessEffect::quadVao_ = 0;
GLuint PostProcessEffect::quadVbo_ = 0;
bool PostProcessEffect::quadInitialized_ = false;
// ============================================================================
// PostProcessEffect实现
// ============================================================================
PostProcessEffect::PostProcessEffect(const std::string &name) : name_(name) {}
bool PostProcessEffect::init() {
initQuad();
valid_ = true;
return true;
}
void PostProcessEffect::shutdown() {
shader_.reset();
valid_ = false;
}
void PostProcessEffect::apply(const Texture &source, RenderTarget &target,
RenderBackend &renderer) {
if (!enabled_ || !valid_)
return;
target.bind();
if (shader_) {
shader_->bind();
shader_->setInt("u_texture", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,
static_cast<GLuint>(
reinterpret_cast<uintptr_t>(source.getNativeHandle())));
onShaderBind(*shader_);
}
renderFullscreenQuad();
if (shader_) {
shader_->unbind();
}
target.unbind();
}
bool PostProcessEffect::loadShader(const std::string &vertSource,
const std::string &fragSource) {
shader_ = std::make_shared<GLShader>();
if (!shader_->compileFromSource(vertSource.c_str(), fragSource.c_str())) {
E2D_ERROR("后处理效果 '{}' 加载Shader失败", name_);
shader_.reset();
return false;
}
return true;
}
bool PostProcessEffect::loadShaderFromFile(const std::string &vertPath,
const std::string &fragPath) {
shader_ = std::make_shared<GLShader>();
if (!shader_->compileFromFile(vertPath, fragPath)) {
E2D_ERROR("后处理效果 '{}' 从文件加载Shader失败", name_);
shader_.reset();
return false;
}
return true;
}
void PostProcessEffect::initQuad() {
if (quadInitialized_)
return;
// 全屏四边形顶点数据(位置和纹理坐标)
float quadVertices[] = {// 位置 // 纹理坐标
-1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f};
glGenVertexArrays(1, &quadVao_);
glGenBuffers(1, &quadVbo_);
glBindVertexArray(quadVao_);
glBindBuffer(GL_ARRAY_BUFFER, quadVbo_);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices,
GL_STATIC_DRAW);
// 位置属性
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
// 纹理坐标属性
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
(void *)(2 * sizeof(float)));
glBindVertexArray(0);
quadInitialized_ = true;
}
void PostProcessEffect::destroyQuad() {
if (quadVao_ != 0) {
glDeleteVertexArrays(1, &quadVao_);
quadVao_ = 0;
}
if (quadVbo_ != 0) {
glDeleteBuffers(1, &quadVbo_);
quadVbo_ = 0;
}
quadInitialized_ = false;
}
void PostProcessEffect::renderFullscreenQuad() {
if (!quadInitialized_) {
initQuad();
}
glBindVertexArray(quadVao_);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
// ============================================================================
// PostProcessStack实现
// ============================================================================
PostProcessStack::PostProcessStack() = default;
PostProcessStack::~PostProcessStack() { shutdown(); }
bool PostProcessStack::init(int width, int height) {
E2D_INFO("初始化后处理栈...");
width_ = width;
height_ = height;
// 创建两个渲染目标用于乒乓渲染
RenderTargetConfig config;
config.width = width;
config.height = height;
config.hasDepthBuffer = false;
config.autoResize = false;
renderTargetA_ = std::make_shared<RenderTarget>();
renderTargetB_ = std::make_shared<RenderTarget>();
if (!renderTargetA_->init(config)) {
E2D_ERROR("创建后处理渲染目标A失败");
return false;
}
if (!renderTargetB_->init(config)) {
E2D_ERROR("创建后处理渲染目标B失败");
return false;
}
valid_ = true;
E2D_INFO("后处理栈初始化成功");
return true;
}
void PostProcessStack::shutdown() {
E2D_INFO("关闭后处理栈...");
clearEffects();
if (renderTargetA_) {
renderTargetA_->shutdown();
renderTargetA_.reset();
}
if (renderTargetB_) {
renderTargetB_->shutdown();
renderTargetB_.reset();
}
valid_ = false;
}
void PostProcessStack::addEffect(Ptr<PostProcessEffect> effect) {
if (effect && effect->init()) {
effects_.push_back(effect);
E2D_INFO("添加后处理效果: {}", effect->getName());
}
}
void PostProcessStack::insertEffect(size_t index,
Ptr<PostProcessEffect> effect) {
if (effect && effect->init() && index <= effects_.size()) {
effects_.insert(effects_.begin() + index, effect);
E2D_INFO("插入后处理效果 '{}' 到位置 {}", effect->getName(), index);
}
}
void PostProcessStack::removeEffect(const std::string &name) {
for (auto it = effects_.begin(); it != effects_.end(); ++it) {
if ((*it)->getName() == name) {
(*it)->shutdown();
effects_.erase(it);
E2D_INFO("移除后处理效果: {}", name);
return;
}
}
}
void PostProcessStack::removeEffect(size_t index) {
if (index < effects_.size()) {
effects_[index]->shutdown();
effects_.erase(effects_.begin() + index);
}
}
Ptr<PostProcessEffect> PostProcessStack::getEffect(const std::string &name) {
for (auto &effect : effects_) {
if (effect->getName() == name) {
return effect;
}
}
return nullptr;
}
Ptr<PostProcessEffect> PostProcessStack::getEffect(size_t index) {
if (index < effects_.size()) {
return effects_[index];
}
return nullptr;
}
void PostProcessStack::clearEffects() {
for (auto &effect : effects_) {
effect->shutdown();
}
effects_.clear();
}
void PostProcessStack::beginCapture() {
if (!valid_)
return;
renderTargetA_->bind();
renderTargetA_->clear(Colors::Black);
capturing_ = true;
}
void PostProcessStack::endCapture(RenderBackend &renderer) {
if (!valid_ || !capturing_)
return;
renderTargetA_->unbind();
// 应用所有后处理效果
if (effects_.empty()) {
// 没有效果,直接渲染到屏幕
// 这里需要渲染renderTargetA_的纹理到屏幕
capturing_ = false;
return;
}
// 乒乓渲染
RenderTarget *readTarget = renderTargetA_.get();
RenderTarget *writeTarget = renderTargetB_.get();
for (size_t i = 0; i < effects_.size(); ++i) {
auto &effect = effects_[i];
if (effect->isEnabled()) {
effect->apply(*readTarget->getColorTexture(), *writeTarget, renderer);
}
// 交换读写目标
std::swap(readTarget, writeTarget);
}
// 最终结果在readTarget中因为最后一次交换
// 这里应该将结果渲染到屏幕
capturing_ = false;
}
void PostProcessStack::process(const Texture &source, RenderTarget &target,
RenderBackend &renderer) {
if (!valid_)
return;
RenderTarget *readTarget = nullptr;
RenderTarget *writeTarget = nullptr;
// 确定读写目标
if (target.getFBO() == renderTargetA_->getFBO()) {
readTarget = renderTargetB_.get();
writeTarget = renderTargetA_.get();
} else {
readTarget = renderTargetA_.get();
writeTarget = renderTargetB_.get();
}
// 首先将源纹理复制到读目标
readTarget->bind();
// 这里需要渲染源纹理到readTarget
readTarget->unbind();
// 应用效果
for (auto &effect : effects_) {
if (effect->isEnabled()) {
effect->apply(*readTarget->getColorTexture(), *writeTarget, renderer);
}
std::swap(readTarget, writeTarget);
}
// 将最终结果复制到目标
readTarget->blitTo(target, true, false);
}
void PostProcessStack::resize(int width, int height) {
if (width_ == width && height_ == height)
return;
width_ = width;
height_ = height;
if (renderTargetA_) {
renderTargetA_->resize(width, height);
}
if (renderTargetB_) {
renderTargetB_->resize(width, height);
}
}
// ============================================================================
// PostProcessManager实现
// ============================================================================
PostProcessManager &PostProcessManager::getInstance() {
static PostProcessManager instance;
return instance;
}
void PostProcessManager::init(int width, int height) {
if (initialized_)
return;
E2D_INFO("初始化后处理管理器...");
mainStack_.init(width, height);
initialized_ = true;
}
void PostProcessManager::shutdown() {
if (!initialized_)
return;
E2D_INFO("关闭后处理管理器...");
mainStack_.shutdown();
initialized_ = false;
}
void PostProcessManager::resize(int width, int height) {
if (initialized_) {
mainStack_.resize(width, height);
}
}
void PostProcessManager::beginFrame() {
if (initialized_) {
mainStack_.beginCapture();
}
}
void PostProcessManager::endFrame(RenderBackend &renderer) {
if (initialized_) {
mainStack_.endCapture(renderer);
}
}
} // namespace extra2d

File diff suppressed because one or more lines are too long