refactor(效果系统): 移除效果相关代码
移除后处理、粒子系统和自定义效果管理器的头文件和实现文件 删除extra2d.h中对效果模块的包含
This commit is contained in:
parent
f08a0bf583
commit
f9c8f080eb
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
Loading…
Reference in New Issue