Compare commits
42 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
583e866861 | |
|
|
edd47a890a | |
|
|
69606230da | |
|
|
6babd376c9 | |
|
|
2748e80dea | |
|
|
c8a6ea19e3 | |
|
|
32e12b8c99 | |
|
|
6b4ce69657 | |
|
|
30b677f192 | |
|
|
89fb955eb0 | |
|
|
f1cf6a6d85 | |
|
|
a4276e4376 | |
|
|
d2660a86bb | |
|
|
61dea772f7 | |
|
|
4f02ad0e39 | |
|
|
4b1de5e36a | |
|
|
9f83b8fde5 | |
|
|
0425425ec7 | |
|
|
6273f3235d | |
|
|
b55d279611 | |
|
|
867013f6eb | |
|
|
78680138c2 | |
|
|
0700bad5d9 | |
|
|
2d8b98bba6 | |
|
|
3216a190ce | |
|
|
6c6cac55f7 | |
|
|
8c56c29cd2 | |
|
|
453a057c7d | |
|
|
f8ecf7e03a | |
|
|
475ae50d2a | |
|
|
269f2d907d | |
|
|
8f7f1612eb | |
|
|
38148a6c54 | |
|
|
34fe0bafcb | |
|
|
9439e200d7 | |
|
|
387ea62853 | |
|
|
c6c90a7374 | |
|
|
93d07e547f | |
|
|
2767d64bf8 | |
|
|
b949d1a3da | |
|
|
55c66e5038 | |
|
|
313a56bf72 |
|
|
@ -1,172 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Node;
|
||||
|
||||
/**
|
||||
* @brief 动作状态枚举
|
||||
*/
|
||||
enum class ActionState {
|
||||
Idle,
|
||||
Running,
|
||||
Paused,
|
||||
Completed
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 动作基类
|
||||
*
|
||||
* 所有动作的基类,定义了动作的核心接口。
|
||||
* 动作用于修改 Node 的属性,实现动画效果。
|
||||
*/
|
||||
class Action {
|
||||
public:
|
||||
using CompletionCallback = std::function<void()>;
|
||||
|
||||
Action();
|
||||
virtual ~Action() = default;
|
||||
|
||||
Action(const Action&) = delete;
|
||||
Action& operator=(const Action&) = delete;
|
||||
Action(Action&&) = default;
|
||||
Action& operator=(Action&&) = default;
|
||||
|
||||
/**
|
||||
* @brief 检查动作是否完成
|
||||
* @return true 如果动作已完成
|
||||
*/
|
||||
virtual bool isDone() const;
|
||||
|
||||
/**
|
||||
* @brief 使用目标节点启动动作
|
||||
* @param target 目标节点
|
||||
*/
|
||||
virtual void startWithTarget(Node* target);
|
||||
|
||||
/**
|
||||
* @brief 停止动作
|
||||
*/
|
||||
virtual void stop();
|
||||
|
||||
/**
|
||||
* @brief 每帧调用的步进函数
|
||||
* @param dt 帧时间间隔
|
||||
*/
|
||||
virtual void step(float dt);
|
||||
|
||||
/**
|
||||
* @brief 更新动作状态
|
||||
* @param time 归一化时间 [0, 1]
|
||||
*/
|
||||
virtual void update(float time);
|
||||
|
||||
/**
|
||||
* @brief 克隆动作
|
||||
* @return 动作的深拷贝
|
||||
*/
|
||||
virtual Action* clone() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建反向动作
|
||||
* @return 反向动作
|
||||
*/
|
||||
virtual Action* reverse() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 暂停动作
|
||||
*/
|
||||
void pause();
|
||||
|
||||
/**
|
||||
* @brief 恢复动作
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* @brief 重启动作
|
||||
*/
|
||||
void restart();
|
||||
|
||||
/**
|
||||
* @brief 设置完成回调
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
void setCompletionCallback(const CompletionCallback& callback) {
|
||||
completionCallback_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取目标节点
|
||||
* @return 目标节点指针
|
||||
*/
|
||||
Node* getTarget() const { return target_; }
|
||||
|
||||
/**
|
||||
* @brief 获取原始目标节点
|
||||
* @return 原始目标节点指针
|
||||
*/
|
||||
Node* getOriginalTarget() const { return originalTarget_; }
|
||||
|
||||
/**
|
||||
* @brief 获取动作状态
|
||||
* @return 当前状态
|
||||
*/
|
||||
ActionState getState() const { return state_; }
|
||||
|
||||
/**
|
||||
* @brief 获取标签
|
||||
* @return 标签值
|
||||
*/
|
||||
int getTag() const { return tag_; }
|
||||
|
||||
/**
|
||||
* @brief 设置标签
|
||||
* @param tag 标签值
|
||||
*/
|
||||
void setTag(int tag) { tag_ = tag; }
|
||||
|
||||
/**
|
||||
* @brief 获取标志位
|
||||
* @return 标志位
|
||||
*/
|
||||
unsigned int getFlags() const { return flags_; }
|
||||
|
||||
/**
|
||||
* @brief 设置标志位
|
||||
* @param flags 标志位
|
||||
*/
|
||||
void setFlags(unsigned int flags) { flags_ = flags; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 动作开始时调用
|
||||
*/
|
||||
virtual void onStart() {}
|
||||
|
||||
/**
|
||||
* @brief 动作完成时调用
|
||||
*/
|
||||
virtual void onComplete() {
|
||||
if (completionCallback_) {
|
||||
completionCallback_();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置动作完成状态
|
||||
*/
|
||||
void setDone() { state_ = ActionState::Completed; }
|
||||
|
||||
Node* target_ = nullptr;
|
||||
Node* originalTarget_ = nullptr;
|
||||
ActionState state_ = ActionState::Idle;
|
||||
int tag_ = -1;
|
||||
unsigned int flags_ = 0;
|
||||
CompletionCallback completionCallback_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,344 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/action_interval.h>
|
||||
#include <extra2d/action/ease.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 缓动动作基类
|
||||
*
|
||||
* 使用装饰器模式包装其他动作,实现缓动效果。
|
||||
*/
|
||||
class ActionEase : public ActionInterval {
|
||||
public:
|
||||
virtual ~ActionEase();
|
||||
|
||||
/**
|
||||
* @brief 获取内部动作
|
||||
* @return 内部动作指针
|
||||
*/
|
||||
ActionInterval* getInnerAction() const { return innerAction_; }
|
||||
|
||||
void startWithTarget(Node* target) override;
|
||||
void stop() override;
|
||||
void update(float time) override;
|
||||
ActionInterval* clone() const override = 0;
|
||||
ActionInterval* reverse() const override = 0;
|
||||
|
||||
protected:
|
||||
ActionEase() = default;
|
||||
bool initWithAction(ActionInterval* action);
|
||||
void onUpdate(float progress) override {}
|
||||
|
||||
ActionInterval* innerAction_ = nullptr;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 指数缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseExponentialIn : public ActionEase {
|
||||
public:
|
||||
static EaseExponentialIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseExponentialOut : public ActionEase {
|
||||
public:
|
||||
static EaseExponentialOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseExponentialInOut : public ActionEase {
|
||||
public:
|
||||
static EaseExponentialInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 正弦缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseSineIn : public ActionEase {
|
||||
public:
|
||||
static EaseSineIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseSineOut : public ActionEase {
|
||||
public:
|
||||
static EaseSineOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseSineInOut : public ActionEase {
|
||||
public:
|
||||
static EaseSineInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 弹性缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseElasticIn : public ActionEase {
|
||||
public:
|
||||
static EaseElasticIn* create(ActionInterval* action, float period = 0.3f);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
|
||||
protected:
|
||||
float period_ = 0.3f;
|
||||
};
|
||||
|
||||
class EaseElasticOut : public ActionEase {
|
||||
public:
|
||||
static EaseElasticOut* create(ActionInterval* action, float period = 0.3f);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
|
||||
protected:
|
||||
float period_ = 0.3f;
|
||||
};
|
||||
|
||||
class EaseElasticInOut : public ActionEase {
|
||||
public:
|
||||
static EaseElasticInOut* create(ActionInterval* action, float period = 0.3f);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
|
||||
protected:
|
||||
float period_ = 0.3f;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 弹跳缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseBounceIn : public ActionEase {
|
||||
public:
|
||||
static EaseBounceIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseBounceOut : public ActionEase {
|
||||
public:
|
||||
static EaseBounceOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseBounceInOut : public ActionEase {
|
||||
public:
|
||||
static EaseBounceInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 回震缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseBackIn : public ActionEase {
|
||||
public:
|
||||
static EaseBackIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseBackOut : public ActionEase {
|
||||
public:
|
||||
static EaseBackOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseBackInOut : public ActionEase {
|
||||
public:
|
||||
static EaseBackInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 二次缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseQuadIn : public ActionEase {
|
||||
public:
|
||||
static EaseQuadIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseQuadOut : public ActionEase {
|
||||
public:
|
||||
static EaseQuadOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseQuadInOut : public ActionEase {
|
||||
public:
|
||||
static EaseQuadInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 三次缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseCubicIn : public ActionEase {
|
||||
public:
|
||||
static EaseCubicIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseCubicOut : public ActionEase {
|
||||
public:
|
||||
static EaseCubicOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseCubicInOut : public ActionEase {
|
||||
public:
|
||||
static EaseCubicInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 四次缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseQuartIn : public ActionEase {
|
||||
public:
|
||||
static EaseQuartIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseQuartOut : public ActionEase {
|
||||
public:
|
||||
static EaseQuartOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseQuartInOut : public ActionEase {
|
||||
public:
|
||||
static EaseQuartInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 五次缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseQuintIn : public ActionEase {
|
||||
public:
|
||||
static EaseQuintIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseQuintOut : public ActionEase {
|
||||
public:
|
||||
static EaseQuintOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseQuintInOut : public ActionEase {
|
||||
public:
|
||||
static EaseQuintInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 圆形缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseCircleIn : public ActionEase {
|
||||
public:
|
||||
static EaseCircleIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseCircleOut : public ActionEase {
|
||||
public:
|
||||
static EaseCircleOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseCircleInOut : public ActionEase {
|
||||
public:
|
||||
static EaseCircleInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 自定义缓动
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 自定义缓动动作
|
||||
*/
|
||||
class EaseCustom : public ActionEase {
|
||||
public:
|
||||
static EaseCustom* create(ActionInterval* action, EaseFunction easeFunc);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
|
||||
protected:
|
||||
EaseFunction easeFunc_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/finite_time_action.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 瞬时动作基类
|
||||
*
|
||||
* 瞬间完成的动作,中间没有任何动画效果。
|
||||
* 继承自 FiniteTimeAction,持续时间为 0。
|
||||
*/
|
||||
class ActionInstant : public FiniteTimeAction {
|
||||
public:
|
||||
ActionInstant();
|
||||
virtual ~ActionInstant() = default;
|
||||
|
||||
/**
|
||||
* @brief 检查动作是否完成
|
||||
* @return 总是返回 true
|
||||
*/
|
||||
bool isDone() const override;
|
||||
|
||||
/**
|
||||
* @brief 使用目标节点启动动作
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void startWithTarget(Node* target) override;
|
||||
|
||||
/**
|
||||
* @brief 每帧调用的步进函数
|
||||
* @param dt 帧时间间隔
|
||||
*/
|
||||
void step(float dt) override;
|
||||
|
||||
/**
|
||||
* @brief 克隆动作
|
||||
* @return 动作的深拷贝
|
||||
*/
|
||||
ActionInstant* clone() const override = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建反向动作
|
||||
* @return 反向动作
|
||||
*/
|
||||
ActionInstant* reverse() const override = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 执行瞬时动作
|
||||
*/
|
||||
virtual void execute() = 0;
|
||||
|
||||
bool done_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/action_instant.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <functional>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 回调动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 无参数回调动作
|
||||
*/
|
||||
class CallFunc : public ActionInstant {
|
||||
public:
|
||||
using Callback = std::function<void()>;
|
||||
|
||||
static CallFunc* create(const Callback& callback);
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
|
||||
Callback callback_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 带节点参数的回调动作
|
||||
*/
|
||||
class CallFuncN : public ActionInstant {
|
||||
public:
|
||||
using Callback = std::function<void(Node*)>;
|
||||
|
||||
static CallFuncN* create(const Callback& callback);
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
|
||||
Callback callback_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 位置动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 瞬间放置到指定位置
|
||||
*/
|
||||
class Place : public ActionInstant {
|
||||
public:
|
||||
static Place* create(const Vec2& position);
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
|
||||
Vec2 position_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 翻转动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief X轴翻转动作
|
||||
*/
|
||||
class FlipX : public ActionInstant {
|
||||
public:
|
||||
static FlipX* create(bool flipX);
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
|
||||
bool flipX_ = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Y轴翻转动作
|
||||
*/
|
||||
class FlipY : public ActionInstant {
|
||||
public:
|
||||
static FlipY* create(bool flipY);
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
|
||||
bool flipY_ = false;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 可见性动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 显示动作
|
||||
*/
|
||||
class Show : public ActionInstant {
|
||||
public:
|
||||
static Show* create();
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 隐藏动作
|
||||
*/
|
||||
class Hide : public ActionInstant {
|
||||
public:
|
||||
static Hide* create();
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 切换可见性动作
|
||||
*/
|
||||
class ToggleVisibility : public ActionInstant {
|
||||
public:
|
||||
static ToggleVisibility* create();
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 节点管理动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 移除自身动作
|
||||
*/
|
||||
class RemoveSelf : public ActionInstant {
|
||||
public:
|
||||
static RemoveSelf* create();
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/finite_time_action.h>
|
||||
#include <extra2d/action/ease.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 时间间隔动作基类
|
||||
*
|
||||
* 在指定时间内完成的动作,中间会有动画效果。
|
||||
* 继承自 FiniteTimeAction。
|
||||
*/
|
||||
class ActionInterval : public FiniteTimeAction {
|
||||
public:
|
||||
ActionInterval() = default;
|
||||
explicit ActionInterval(float duration);
|
||||
virtual ~ActionInterval() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取已流逝时间
|
||||
* @return 已流逝时间(秒)
|
||||
*/
|
||||
float getElapsed() const { return elapsed_; }
|
||||
|
||||
/**
|
||||
* @brief 检查动作是否完成
|
||||
* @return true 如果动作已完成
|
||||
*/
|
||||
bool isDone() const override;
|
||||
|
||||
/**
|
||||
* @brief 使用目标节点启动动作
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void startWithTarget(Node* target) override;
|
||||
|
||||
/**
|
||||
* @brief 停止动作
|
||||
*/
|
||||
void stop() override;
|
||||
|
||||
/**
|
||||
* @brief 每帧调用的步进函数
|
||||
* @param dt 帧时间间隔
|
||||
*/
|
||||
void step(float dt) override;
|
||||
|
||||
/**
|
||||
* @brief 设置振幅比率(用于缓动)
|
||||
* @param amp 振幅比率
|
||||
*/
|
||||
void setAmplitudeRate(float amp) { amplitudeRate_ = amp; }
|
||||
|
||||
/**
|
||||
* @brief 获取振幅比率
|
||||
* @return 振幅比率
|
||||
*/
|
||||
float getAmplitudeRate() const { return amplitudeRate_; }
|
||||
|
||||
/**
|
||||
* @brief 设置内置缓动函数
|
||||
* @param easeFunc 缓动函数
|
||||
*/
|
||||
void setEaseFunction(EaseFunction easeFunc) { easeFunc_ = easeFunc; }
|
||||
|
||||
/**
|
||||
* @brief 获取内置缓动函数
|
||||
* @return 缓动函数
|
||||
*/
|
||||
EaseFunction getEaseFunction() const { return easeFunc_; }
|
||||
|
||||
/**
|
||||
* @brief 克隆动作
|
||||
* @return 动作的深拷贝
|
||||
*/
|
||||
ActionInterval* clone() const override = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建反向动作
|
||||
* @return 反向动作
|
||||
*/
|
||||
ActionInterval* reverse() const override = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 动作开始时调用
|
||||
*/
|
||||
virtual void onStart() {}
|
||||
|
||||
/**
|
||||
* @brief 动作更新时调用
|
||||
* @param progress 归一化进度 [0, 1]
|
||||
*/
|
||||
virtual void onUpdate(float progress) = 0;
|
||||
|
||||
float elapsed_ = 0.0f;
|
||||
bool firstTick_ = true;
|
||||
float amplitudeRate_ = 1.0f;
|
||||
EaseFunction easeFunc_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,469 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/action_interval.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 移动动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 相对移动动作
|
||||
*/
|
||||
class MoveBy : public ActionInterval {
|
||||
public:
|
||||
static MoveBy* create(float duration, const Vec2& delta);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
Vec2 delta_;
|
||||
Vec2 startPosition_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 绝对移动动作
|
||||
*/
|
||||
class MoveTo : public ActionInterval {
|
||||
public:
|
||||
static MoveTo* create(float duration, const Vec2& position);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
Vec2 endPosition_;
|
||||
Vec2 startPosition_;
|
||||
Vec2 delta_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 跳跃动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 相对跳跃动作
|
||||
*/
|
||||
class JumpBy : public ActionInterval {
|
||||
public:
|
||||
static JumpBy* create(float duration, const Vec2& position, float height, int jumps);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
Vec2 startPosition_;
|
||||
Vec2 delta_;
|
||||
float height_ = 0.0f;
|
||||
int jumps_ = 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 绝对跳跃动作
|
||||
*/
|
||||
class JumpTo : public JumpBy {
|
||||
public:
|
||||
static JumpTo* create(float duration, const Vec2& position, float height, int jumps);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
|
||||
Vec2 endPosition_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 贝塞尔曲线动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 贝塞尔曲线配置
|
||||
*/
|
||||
struct BezierConfig {
|
||||
Vec2 controlPoint1;
|
||||
Vec2 controlPoint2;
|
||||
Vec2 endPosition;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 相对贝塞尔曲线动作
|
||||
*/
|
||||
class BezierBy : public ActionInterval {
|
||||
public:
|
||||
static BezierBy* create(float duration, const BezierConfig& config);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
static float bezierat(float a, float b, float c, float d, float t);
|
||||
|
||||
BezierConfig config_;
|
||||
Vec2 startPosition_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 绝对贝塞尔曲线动作
|
||||
*/
|
||||
class BezierTo : public BezierBy {
|
||||
public:
|
||||
static BezierTo* create(float duration, const BezierConfig& config);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
|
||||
BezierConfig originalConfig_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 缩放动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 相对缩放动作
|
||||
*/
|
||||
class ScaleBy : public ActionInterval {
|
||||
public:
|
||||
static ScaleBy* create(float duration, float scale);
|
||||
static ScaleBy* create(float duration, float scaleX, float scaleY);
|
||||
static ScaleBy* create(float duration, const Vec2& scale);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
Vec2 deltaScale_;
|
||||
Vec2 startScale_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 绝对缩放动作
|
||||
*/
|
||||
class ScaleTo : public ActionInterval {
|
||||
public:
|
||||
static ScaleTo* create(float duration, float scale);
|
||||
static ScaleTo* create(float duration, float scaleX, float scaleY);
|
||||
static ScaleTo* create(float duration, const Vec2& scale);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
Vec2 endScale_;
|
||||
Vec2 startScale_;
|
||||
Vec2 delta_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 旋转动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 相对旋转动作
|
||||
*/
|
||||
class RotateBy : public ActionInterval {
|
||||
public:
|
||||
static RotateBy* create(float duration, float deltaAngle);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
float deltaAngle_ = 0.0f;
|
||||
float startAngle_ = 0.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 绝对旋转动作
|
||||
*/
|
||||
class RotateTo : public ActionInterval {
|
||||
public:
|
||||
static RotateTo* create(float duration, float angle);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
float endAngle_ = 0.0f;
|
||||
float startAngle_ = 0.0f;
|
||||
float deltaAngle_ = 0.0f;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 淡入淡出动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 淡入动作
|
||||
*/
|
||||
class FadeIn : public ActionInterval {
|
||||
public:
|
||||
static FadeIn* create(float duration);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
float startOpacity_ = 0.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 淡出动作
|
||||
*/
|
||||
class FadeOut : public ActionInterval {
|
||||
public:
|
||||
static FadeOut* create(float duration);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
float startOpacity_ = 0.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 淡入到指定透明度动作
|
||||
*/
|
||||
class FadeTo : public ActionInterval {
|
||||
public:
|
||||
static FadeTo* create(float duration, float opacity);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
float endOpacity_ = 0.0f;
|
||||
float startOpacity_ = 0.0f;
|
||||
float deltaOpacity_ = 0.0f;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 闪烁动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 闪烁动作
|
||||
*/
|
||||
class Blink : public ActionInterval {
|
||||
public:
|
||||
static Blink* create(float duration, int times);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
int times_ = 1;
|
||||
int currentTimes_ = 0;
|
||||
bool originalVisible_ = true;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 色调动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 色调变化动作
|
||||
*/
|
||||
class TintTo : public ActionInterval {
|
||||
public:
|
||||
static TintTo* create(float duration, uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
Color3B startColor_;
|
||||
Color3B endColor_;
|
||||
Color3B deltaColor_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 相对色调变化动作
|
||||
*/
|
||||
class TintBy : public ActionInterval {
|
||||
public:
|
||||
static TintBy* create(float duration, int16_t deltaRed, int16_t deltaGreen, int16_t deltaBlue);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
Color3B startColor_;
|
||||
int16_t deltaR_ = 0;
|
||||
int16_t deltaG_ = 0;
|
||||
int16_t deltaB_ = 0;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 组合动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 序列动作
|
||||
*/
|
||||
class Sequence : public ActionInterval {
|
||||
public:
|
||||
static Sequence* create(ActionInterval* action1, ...);
|
||||
static Sequence* create(const std::vector<ActionInterval*>& actions);
|
||||
|
||||
~Sequence();
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
std::vector<ActionInterval*> actions_;
|
||||
size_t currentIndex_ = 0;
|
||||
float split_ = 0.0f;
|
||||
float last_ = -1.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 并行动作
|
||||
*/
|
||||
class Spawn : public ActionInterval {
|
||||
public:
|
||||
static Spawn* create(ActionInterval* action1, ...);
|
||||
static Spawn* create(const std::vector<ActionInterval*>& actions);
|
||||
|
||||
~Spawn();
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
std::vector<ActionInterval*> actions_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 重复动作
|
||||
*/
|
||||
class Repeat : public ActionInterval {
|
||||
public:
|
||||
static Repeat* create(ActionInterval* action, int times);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
bool isDone() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
ActionInterval* innerAction_ = nullptr;
|
||||
int times_ = 1;
|
||||
int currentTimes_ = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 永久重复动作
|
||||
*/
|
||||
class RepeatForever : public ActionInterval {
|
||||
public:
|
||||
static RepeatForever* create(ActionInterval* action);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
bool isDone() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
ActionInterval* innerAction_ = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 延时动作
|
||||
*/
|
||||
class DelayTime : public ActionInterval {
|
||||
public:
|
||||
static DelayTime* create(float duration);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onUpdate(float progress) override {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 反向时间动作
|
||||
*/
|
||||
class ReverseTime : public ActionInterval {
|
||||
public:
|
||||
static ReverseTime* create(ActionInterval* action);
|
||||
|
||||
~ReverseTime();
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
ActionInterval* innerAction_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/action.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 动作管理器
|
||||
*
|
||||
* 单例模式,集中管理所有动作的调度和生命周期。
|
||||
* 负责动作的添加、移除、暂停、恢复和更新。
|
||||
*/
|
||||
class ActionManager {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return ActionManager 实例指针
|
||||
*/
|
||||
static ActionManager* getInstance();
|
||||
|
||||
/**
|
||||
* @brief 销毁单例实例
|
||||
*/
|
||||
static void destroyInstance();
|
||||
|
||||
/**
|
||||
* @brief 添加动作到管理器
|
||||
* @param action 动作指针
|
||||
* @param target 目标节点
|
||||
* @param paused 是否暂停
|
||||
*/
|
||||
void addAction(Action* action, Node* target, bool paused = false);
|
||||
|
||||
/**
|
||||
* @brief 移除指定动作
|
||||
* @param action 动作指针
|
||||
*/
|
||||
void removeAction(Action* action);
|
||||
|
||||
/**
|
||||
* @brief 根据标签移除动作
|
||||
* @param tag 标签值
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void removeActionByTag(int tag, Node* target);
|
||||
|
||||
/**
|
||||
* @brief 根据标志位移除动作
|
||||
* @param flags 标志位
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void removeActionsByFlags(unsigned int flags, Node* target);
|
||||
|
||||
/**
|
||||
* @brief 移除目标节点的所有动作
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void removeAllActionsFromTarget(Node* target);
|
||||
|
||||
/**
|
||||
* @brief 移除所有动作
|
||||
*/
|
||||
void removeAllActions();
|
||||
|
||||
/**
|
||||
* @brief 根据标签获取动作
|
||||
* @param tag 标签值
|
||||
* @param target 目标节点
|
||||
* @return 动作指针,未找到返回 nullptr
|
||||
*/
|
||||
Action* getActionByTag(int tag, Node* target);
|
||||
|
||||
/**
|
||||
* @brief 获取目标节点的动作数量
|
||||
* @param target 目标节点
|
||||
* @return 动作数量
|
||||
*/
|
||||
size_t getActionCount(Node* target) const;
|
||||
|
||||
/**
|
||||
* @brief 暂停目标节点的所有动作
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void pauseTarget(Node* target);
|
||||
|
||||
/**
|
||||
* @brief 恢复目标节点的所有动作
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void resumeTarget(Node* target);
|
||||
|
||||
/**
|
||||
* @brief 检查目标节点是否暂停
|
||||
* @param target 目标节点
|
||||
* @return true 如果暂停
|
||||
*/
|
||||
bool isPaused(Node* target) const;
|
||||
|
||||
/**
|
||||
* @brief 更新所有动作(每帧调用)
|
||||
* @param dt 帧时间间隔
|
||||
*/
|
||||
void update(float dt);
|
||||
|
||||
private:
|
||||
ActionManager();
|
||||
~ActionManager();
|
||||
|
||||
struct ActionElement {
|
||||
std::vector<Action*> actions;
|
||||
Node* target = nullptr;
|
||||
bool paused = false;
|
||||
int actionIndex = 0;
|
||||
Action* currentAction = nullptr;
|
||||
bool currentActionSalvaged = false;
|
||||
};
|
||||
|
||||
using ActionMap = std::unordered_map<Node*, ActionElement>;
|
||||
|
||||
void removeActionAt(size_t index, ActionElement& element);
|
||||
void deleteAction(Action* action);
|
||||
|
||||
ActionMap targets_;
|
||||
static ActionManager* instance_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/action.h>
|
||||
#include <extra2d/action/action_interval.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 速度控制动作
|
||||
*
|
||||
* 包装其他动作,实现动态速度控制。
|
||||
* 可以在运行时调整动作的播放速度。
|
||||
*/
|
||||
class Speed : public Action {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建速度控制动作
|
||||
* @param action 内部动作
|
||||
* @param speed 速度倍率(1.0 为正常速度)
|
||||
* @return 动作指针
|
||||
*/
|
||||
static Speed* create(ActionInterval* action, float speed);
|
||||
|
||||
~Speed();
|
||||
|
||||
/**
|
||||
* @brief 获取速度倍率
|
||||
* @return 速度倍率
|
||||
*/
|
||||
float getSpeed() const { return speed_; }
|
||||
|
||||
/**
|
||||
* @brief 设置速度倍率
|
||||
* @param speed 速度倍率
|
||||
*/
|
||||
void setSpeed(float speed) { speed_ = speed; }
|
||||
|
||||
/**
|
||||
* @brief 获取内部动作
|
||||
* @return 内部动作指针
|
||||
*/
|
||||
ActionInterval* getInnerAction() const { return innerAction_; }
|
||||
|
||||
void startWithTarget(Node* target) override;
|
||||
void stop() override;
|
||||
void step(float dt) override;
|
||||
bool isDone() const override;
|
||||
Action* clone() const override;
|
||||
Action* reverse() const override;
|
||||
|
||||
protected:
|
||||
Speed() = default;
|
||||
|
||||
ActionInterval* innerAction_ = nullptr;
|
||||
float speed_ = 1.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 跟随动作
|
||||
*
|
||||
* 使节点跟随另一个节点移动。
|
||||
* 常用于相机跟随玩家。
|
||||
*/
|
||||
class Follow : public Action {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建跟随动作
|
||||
* @param followedNode 被跟随的节点
|
||||
* @return 动作指针
|
||||
*/
|
||||
static Follow* create(Node* followedNode);
|
||||
|
||||
/**
|
||||
* @brief 创建带边界的跟随动作
|
||||
* @param followedNode 被跟随的节点
|
||||
* @param boundary 边界矩形
|
||||
* @return 动作指针
|
||||
*/
|
||||
static Follow* create(Node* followedNode, const Rect& boundary);
|
||||
|
||||
~Follow();
|
||||
|
||||
/**
|
||||
* @brief 获取被跟随的节点
|
||||
* @return 被跟随的节点指针
|
||||
*/
|
||||
Node* getFollowedNode() const { return followedNode_; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否设置了边界
|
||||
* @return true 如果设置了边界
|
||||
*/
|
||||
bool isBoundarySet() const { return boundarySet_; }
|
||||
|
||||
void startWithTarget(Node* target) override;
|
||||
void stop() override;
|
||||
void step(float dt) override;
|
||||
bool isDone() const override;
|
||||
Action* clone() const override;
|
||||
Action* reverse() const override;
|
||||
|
||||
protected:
|
||||
Follow() = default;
|
||||
|
||||
Node* followedNode_ = nullptr;
|
||||
Rect boundary_;
|
||||
bool boundarySet_ = false;
|
||||
Vec2 halfScreenSize_;
|
||||
Vec2 fullScreenSize_;
|
||||
Vec2 leftBoundary_;
|
||||
Vec2 rightBoundary_;
|
||||
Vec2 topBoundary_;
|
||||
Vec2 bottomBoundary_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 目标动作
|
||||
*
|
||||
* 允许在一个节点上运行动作,但目标为另一个节点。
|
||||
*/
|
||||
class TargetedAction : public Action {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建目标动作
|
||||
* @param target 目标节点
|
||||
* @param action 要运行的动作
|
||||
* @return 动作指针
|
||||
*/
|
||||
static TargetedAction* create(Node* target, FiniteTimeAction* action);
|
||||
|
||||
~TargetedAction();
|
||||
|
||||
/**
|
||||
* @brief 获取目标节点
|
||||
* @return 目标节点指针
|
||||
*/
|
||||
Node* getTargetNode() const { return targetNode_; }
|
||||
|
||||
/**
|
||||
* @brief 设置目标节点
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void setTargetNode(Node* target) { targetNode_ = target; }
|
||||
|
||||
/**
|
||||
* @brief 获取内部动作
|
||||
* @return 内部动作指针
|
||||
*/
|
||||
FiniteTimeAction* getAction() const { return innerAction_; }
|
||||
|
||||
void startWithTarget(Node* target) override;
|
||||
void stop() override;
|
||||
void step(float dt) override;
|
||||
bool isDone() const override;
|
||||
Action* clone() const override;
|
||||
Action* reverse() const override;
|
||||
|
||||
protected:
|
||||
TargetedAction() = default;
|
||||
|
||||
Node* targetNode_ = nullptr;
|
||||
FiniteTimeAction* innerAction_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 缓动函数类型
|
||||
*/
|
||||
using EaseFunction = float (*)(float);
|
||||
|
||||
// ============================================================================
|
||||
// 线性缓动
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 线性缓动(无缓动)
|
||||
* @param t 归一化时间 [0, 1]
|
||||
* @return 缓动后的值
|
||||
*/
|
||||
float easeLinear(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 二次缓动 (Quad)
|
||||
// ============================================================================
|
||||
|
||||
float easeInQuad(float t);
|
||||
float easeOutQuad(float t);
|
||||
float easeInOutQuad(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 三次缓动 (Cubic)
|
||||
// ============================================================================
|
||||
|
||||
float easeInCubic(float t);
|
||||
float easeOutCubic(float t);
|
||||
float easeInOutCubic(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 四次缓动 (Quart)
|
||||
// ============================================================================
|
||||
|
||||
float easeInQuart(float t);
|
||||
float easeOutQuart(float t);
|
||||
float easeInOutQuart(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 五次缓动 (Quint)
|
||||
// ============================================================================
|
||||
|
||||
float easeInQuint(float t);
|
||||
float easeOutQuint(float t);
|
||||
float easeInOutQuint(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 正弦缓动 (Sine)
|
||||
// ============================================================================
|
||||
|
||||
float easeInSine(float t);
|
||||
float easeOutSine(float t);
|
||||
float easeInOutSine(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 指数缓动 (Exponential)
|
||||
// ============================================================================
|
||||
|
||||
float easeInExpo(float t);
|
||||
float easeOutExpo(float t);
|
||||
float easeInOutExpo(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 圆形缓动 (Circular)
|
||||
// ============================================================================
|
||||
|
||||
float easeInCirc(float t);
|
||||
float easeOutCirc(float t);
|
||||
float easeInOutCirc(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 回震缓动 (Back)
|
||||
// ============================================================================
|
||||
|
||||
float easeInBack(float t);
|
||||
float easeOutBack(float t);
|
||||
float easeInOutBack(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 弹性缓动 (Elastic)
|
||||
// ============================================================================
|
||||
|
||||
float easeInElastic(float t);
|
||||
float easeOutElastic(float t);
|
||||
float easeInOutElastic(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 弹跳缓动 (Bounce)
|
||||
// ============================================================================
|
||||
|
||||
float easeInBounce(float t);
|
||||
float easeOutBounce(float t);
|
||||
float easeInOutBounce(float t);
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/action.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 有限时间动作基类
|
||||
*
|
||||
* 所有具有持续时间的动作的基类。
|
||||
* 继承自 Action,添加了持续时间属性。
|
||||
*/
|
||||
class FiniteTimeAction : public Action {
|
||||
public:
|
||||
FiniteTimeAction() = default;
|
||||
explicit FiniteTimeAction(float duration);
|
||||
virtual ~FiniteTimeAction() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取动作持续时间
|
||||
* @return 持续时间(秒)
|
||||
*/
|
||||
float getDuration() const { return duration_; }
|
||||
|
||||
/**
|
||||
* @brief 设置动作持续时间
|
||||
* @param duration 持续时间(秒)
|
||||
*/
|
||||
void setDuration(float duration) { duration_ = duration; }
|
||||
|
||||
/**
|
||||
* @brief 克隆动作
|
||||
* @return 动作的深拷贝
|
||||
*/
|
||||
FiniteTimeAction* clone() const override = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建反向动作
|
||||
* @return 反向动作
|
||||
*/
|
||||
FiniteTimeAction* reverse() const override = 0;
|
||||
|
||||
protected:
|
||||
float duration_ = 0.0f;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// ALS 图层信息
|
||||
// ============================================================================
|
||||
struct AlsLayerInfo {
|
||||
std::string aniPath; // 子动画的 ANI 文件路径
|
||||
int zOrder = 0; // 层级顺序
|
||||
Vec2 offset; // 层偏移
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ALS 解析结果
|
||||
// ============================================================================
|
||||
struct AlsParseResult {
|
||||
bool success = false;
|
||||
std::string errorMessage;
|
||||
std::vector<AlsLayerInfo> layers;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// AlsParser - ALS 复合动画文件解析器
|
||||
// 解析 .als 文件获取多层动画的图层信息
|
||||
// ============================================================================
|
||||
class AlsParser {
|
||||
public:
|
||||
AlsParser() = default;
|
||||
|
||||
/// 从文件解析
|
||||
AlsParseResult parse(const std::string &filePath);
|
||||
|
||||
/// 从内存内容解析
|
||||
AlsParseResult parseFromMemory(const std::string &content,
|
||||
const std::string &basePath = "");
|
||||
|
||||
/// 设置基础路径
|
||||
void setBasePath(const std::string &basePath) { basePath_ = basePath; }
|
||||
|
||||
private:
|
||||
std::string basePath_;
|
||||
|
||||
std::string resolvePath(const std::string &relativePath) const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <extra2d/animation/ani_parser.h>
|
||||
#include <extra2d/animation/animation_cache.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// DNF ANI 二进制格式中的节点类型枚举
|
||||
// ============================================================================
|
||||
enum class AniNodeType : uint16_t {
|
||||
Loop = 0,
|
||||
Shadow = 1,
|
||||
Coord = 3,
|
||||
ImageRate = 7,
|
||||
ImageRotate = 8,
|
||||
RGBA = 9,
|
||||
Interpolation = 10,
|
||||
GraphicEffect = 11,
|
||||
Delay = 12,
|
||||
DamageType = 13,
|
||||
DamageBox = 14,
|
||||
AttackBox = 15,
|
||||
PlaySound = 16,
|
||||
Preload = 17,
|
||||
Spectrum = 18,
|
||||
SetFlag = 23,
|
||||
FlipType = 24,
|
||||
LoopStart = 25,
|
||||
LoopEnd = 26,
|
||||
Clip = 27,
|
||||
Operation = 28,
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// AniBinaryParser - ANI 二进制格式解析器
|
||||
// 参考 DNF-Porting 的 PvfAnimation 实现
|
||||
// ============================================================================
|
||||
class AniBinaryParser {
|
||||
public:
|
||||
AniBinaryParser() = default;
|
||||
|
||||
/// 从二进制数据解析
|
||||
AniParseResult parse(const uint8_t *data, size_t length);
|
||||
|
||||
/// 从文件解析
|
||||
AniParseResult parseFromFile(const std::string &filePath);
|
||||
|
||||
/// 设置路径替换回调
|
||||
void setPathResolver(PathResolveCallback callback) {
|
||||
pathResolver_ = std::move(callback);
|
||||
}
|
||||
|
||||
/// 设置基础路径
|
||||
void setBasePath(const std::string &basePath) { basePath_ = basePath; }
|
||||
|
||||
private:
|
||||
PathResolveCallback pathResolver_;
|
||||
std::string basePath_;
|
||||
|
||||
std::string resolvePath(const std::string &relativePath) const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/animation/animation_cache.h>
|
||||
#include <extra2d/animation/animation_clip.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// ANI 文件解析结果
|
||||
// ============================================================================
|
||||
struct AniParseResult {
|
||||
bool success = false;
|
||||
std::string errorMessage;
|
||||
Ptr<AnimationClip> clip;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// AniParser - ANI 脚本文件解析器
|
||||
// 将原始 ANI 文件格式解析为 AnimationClip 数据
|
||||
// ============================================================================
|
||||
class AniParser {
|
||||
public:
|
||||
AniParser() = default;
|
||||
|
||||
/// 从文件解析
|
||||
AniParseResult parse(const std::string &filePath);
|
||||
|
||||
/// 从内存内容解析
|
||||
AniParseResult parseFromMemory(const std::string &content,
|
||||
const std::string &basePath = "");
|
||||
|
||||
/// 设置路径替换回调(对应原始 AdditionalOptions)
|
||||
void setPathResolver(PathResolveCallback callback) {
|
||||
pathResolver_ = std::move(callback);
|
||||
}
|
||||
|
||||
/// 设置基础路径(用于解析相对路径)
|
||||
void setBasePath(const std::string &basePath) { basePath_ = basePath; }
|
||||
|
||||
private:
|
||||
PathResolveCallback pathResolver_;
|
||||
std::string basePath_;
|
||||
|
||||
std::string resolvePath(const std::string &relativePath) const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <extra2d/animation/animation_cache.h>
|
||||
#include <extra2d/animation/animation_controller.h>
|
||||
#include <extra2d/scene/sprite.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// AnimatedSprite - 动画精灵节点
|
||||
// 将 AnimationController 与 Sprite 渲染桥接,接入场景图
|
||||
// ============================================================================
|
||||
class AnimatedSprite : public Sprite {
|
||||
public:
|
||||
AnimatedSprite();
|
||||
~AnimatedSprite() override = default;
|
||||
|
||||
// ------ 静态工厂 ------
|
||||
static Ptr<AnimatedSprite> create();
|
||||
static Ptr<AnimatedSprite> create(Ptr<AnimationClip> clip);
|
||||
static Ptr<AnimatedSprite> create(const std::string &aniFilePath);
|
||||
|
||||
// ------ 动画绑定 ------
|
||||
void setAnimationClip(Ptr<AnimationClip> clip);
|
||||
void loadAnimation(const std::string &aniFilePath);
|
||||
Ptr<AnimationClip> getAnimationClip() const;
|
||||
|
||||
// ------ 动画字典 ------
|
||||
void addAnimation(const std::string &name, Ptr<AnimationClip> clip);
|
||||
void play(const std::string &name, bool loop = true);
|
||||
bool hasAnimation(const std::string &name) const;
|
||||
Ptr<AnimationClip> getAnimation(const std::string &name) const;
|
||||
const std::string &getCurrentAnimationName() const;
|
||||
|
||||
// ------ 播放控制(委托 controller_)------
|
||||
void play();
|
||||
void pause();
|
||||
void resume();
|
||||
void stop();
|
||||
void reset();
|
||||
bool isPlaying() const;
|
||||
bool isPaused() const;
|
||||
bool isStopped() const;
|
||||
|
||||
// ------ 属性控制 ------
|
||||
void setLooping(bool loop);
|
||||
bool isLooping() const;
|
||||
void setPlaybackSpeed(float speed);
|
||||
float getPlaybackSpeed() const;
|
||||
|
||||
// ------ 帧控制 ------
|
||||
void setFrameIndex(size_t index);
|
||||
size_t getCurrentFrameIndex() const;
|
||||
size_t getTotalFrames() const;
|
||||
void nextFrame();
|
||||
void prevFrame();
|
||||
|
||||
// ------ 帧范围限制 ------
|
||||
/// 设置帧播放范围(用于精灵图动画,限制在指定范围内循环)
|
||||
/// @param start 起始帧索引(包含)
|
||||
/// @param end 结束帧索引(包含),-1表示不限制
|
||||
void setFrameRange(int start, int end = -1);
|
||||
|
||||
/// 获取当前帧范围
|
||||
/// @return pair<起始帧, 结束帧>,结束帧为-1表示不限制
|
||||
std::pair<int, int> getFrameRange() const;
|
||||
|
||||
/// 清除帧范围限制(恢复播放所有帧)
|
||||
void clearFrameRange();
|
||||
|
||||
/// 检查是否设置了帧范围限制
|
||||
bool hasFrameRange() const;
|
||||
|
||||
// ------ 回调 ------
|
||||
void setCompletionCallback(AnimationController::CompletionCallback cb);
|
||||
void setKeyframeCallback(AnimationController::KeyframeCallback cb);
|
||||
void setSoundTriggerCallback(AnimationController::SoundTriggerCallback cb);
|
||||
|
||||
// ------ 碰撞盒访问(当前帧)------
|
||||
const std::vector<std::array<int32_t, 6>> &getCurrentDamageBoxes() const;
|
||||
const std::vector<std::array<int32_t, 6>> &getCurrentAttackBoxes() const;
|
||||
|
||||
// ------ 帧变换控制 ------
|
||||
/// 设置是否由动画帧数据覆盖节点的 position/scale/rotation
|
||||
/// ANI 动画需要开启(默认),精灵图动画应关闭
|
||||
void setApplyFrameTransform(bool apply) { applyFrameTransform_ = apply; }
|
||||
bool isApplyFrameTransform() const { return applyFrameTransform_; }
|
||||
|
||||
// ------ 自动播放 ------
|
||||
void setAutoPlay(bool autoPlay) { autoPlay_ = autoPlay; }
|
||||
bool isAutoPlay() const { return autoPlay_; }
|
||||
|
||||
// ------ 直接控制器访问 ------
|
||||
AnimationController &getController() { return controller_; }
|
||||
const AnimationController &getController() const { return controller_; }
|
||||
|
||||
protected:
|
||||
void onUpdate(float dt) override;
|
||||
void onEnter() override;
|
||||
|
||||
private:
|
||||
AnimationController controller_;
|
||||
bool autoPlay_ = false;
|
||||
bool applyFrameTransform_ = true;
|
||||
|
||||
// 动画字典
|
||||
std::unordered_map<std::string, Ptr<AnimationClip>> animations_;
|
||||
std::string currentAnimationName_;
|
||||
static const std::string emptyString_;
|
||||
|
||||
// 帧范围限制(用于精灵图动画)
|
||||
int frameRangeStart_ = 0; // 起始帧索引
|
||||
int frameRangeEnd_ = -1; // 结束帧索引,-1表示不限制
|
||||
|
||||
// 空碰撞盒列表(用于无帧时返回引用)
|
||||
static const std::vector<std::array<int32_t, 6>> emptyBoxes_;
|
||||
|
||||
void applyFrame(const AnimationFrame &frame);
|
||||
void onFrameChanged(size_t oldIdx, size_t newIdx,
|
||||
const AnimationFrame &frame);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/animation/animation_clip.h>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 路径替换回调(对应原始 AdditionalOptions)
|
||||
using PathResolveCallback = std::function<std::string(const std::string &)>;
|
||||
|
||||
// ============================================================================
|
||||
// AnimationCache - 动画片段全局缓存(借鉴 Cocos AnimationCache)
|
||||
// 同一 ANI 文件只解析一次,后续直接复用数据
|
||||
// ============================================================================
|
||||
class AnimationCache {
|
||||
public:
|
||||
static AnimationCache &getInstance() {
|
||||
static AnimationCache instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
// ------ 加载与获取 ------
|
||||
|
||||
/// 从文件加载(自动缓存),已缓存则直接返回
|
||||
/// 注意:实际的 ANI 解析逻辑在 AniParser 中实现
|
||||
/// 此方法在 animation_cache.cpp 中实现,依赖 AniParser
|
||||
Ptr<AnimationClip> loadClip(const std::string &aniFilePath);
|
||||
|
||||
/// 从缓存获取(不触发加载)
|
||||
Ptr<AnimationClip> getClip(const std::string &name) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto it = clips_.find(name);
|
||||
if (it != clips_.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// 手动添加到缓存
|
||||
void addClip(Ptr<AnimationClip> clip, const std::string &name) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
clips_[name] = std::move(clip);
|
||||
}
|
||||
|
||||
// ------ 缓存管理 ------
|
||||
|
||||
bool has(const std::string &name) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return clips_.find(name) != clips_.end();
|
||||
}
|
||||
|
||||
void removeClip(const std::string &name) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
clips_.erase(name);
|
||||
}
|
||||
|
||||
/// 移除未被外部引用的动画片段
|
||||
void removeUnusedClips() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (auto it = clips_.begin(); it != clips_.end();) {
|
||||
if (it->second.use_count() == 1) {
|
||||
it = clips_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
clips_.clear();
|
||||
}
|
||||
|
||||
size_t count() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return clips_.size();
|
||||
}
|
||||
|
||||
// ------ 路径配置 ------
|
||||
void setPathResolver(PathResolveCallback resolver) {
|
||||
pathResolver_ = std::move(resolver);
|
||||
}
|
||||
|
||||
PathResolveCallback getPathResolver() const { return pathResolver_; }
|
||||
|
||||
private:
|
||||
AnimationCache() = default;
|
||||
~AnimationCache() = default;
|
||||
AnimationCache(const AnimationCache &) = delete;
|
||||
AnimationCache &operator=(const AnimationCache &) = delete;
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
std::unordered_map<std::string, Ptr<AnimationClip>> clips_;
|
||||
PathResolveCallback pathResolver_;
|
||||
};
|
||||
|
||||
// 便捷宏
|
||||
#define E2D_ANIMATION_CACHE() ::extra2d::AnimationCache::getInstance()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <extra2d/animation/animation_frame.h>
|
||||
#include <extra2d/animation/sprite_frame_cache.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// AnimationClip - 动画片段(纯数据,可复用)
|
||||
// 借鉴 Cocos:一份 AnimationClip 可被多个 AnimationNode 同时使用
|
||||
// ============================================================================
|
||||
class AnimationClip {
|
||||
public:
|
||||
AnimationClip() = default;
|
||||
|
||||
explicit AnimationClip(const std::string &name) : name_(name) {}
|
||||
|
||||
// ------ 帧管理 ------
|
||||
void addFrame(const AnimationFrame &frame) { frames_.push_back(frame); }
|
||||
|
||||
void addFrame(AnimationFrame &&frame) { frames_.push_back(std::move(frame)); }
|
||||
|
||||
void insertFrame(size_t index, const AnimationFrame &frame) {
|
||||
assert(index <= frames_.size());
|
||||
frames_.insert(frames_.begin() + static_cast<ptrdiff_t>(index), frame);
|
||||
}
|
||||
|
||||
void removeFrame(size_t index) {
|
||||
assert(index < frames_.size());
|
||||
frames_.erase(frames_.begin() + static_cast<ptrdiff_t>(index));
|
||||
}
|
||||
|
||||
void clearFrames() { frames_.clear(); }
|
||||
|
||||
const AnimationFrame &getFrame(size_t index) const {
|
||||
assert(index < frames_.size());
|
||||
return frames_[index];
|
||||
}
|
||||
|
||||
AnimationFrame &getFrame(size_t index) {
|
||||
assert(index < frames_.size());
|
||||
return frames_[index];
|
||||
}
|
||||
|
||||
size_t getFrameCount() const { return frames_.size(); }
|
||||
bool empty() const { return frames_.empty(); }
|
||||
|
||||
// ------ 全局属性(对应原始 AnimationFlag)------
|
||||
FramePropertySet &globalProperties() { return globalProperties_; }
|
||||
const FramePropertySet &globalProperties() const { return globalProperties_; }
|
||||
|
||||
bool isLooping() const {
|
||||
return globalProperties_.getOr<bool>(FramePropertyKey::Loop, false);
|
||||
}
|
||||
|
||||
void setLooping(bool loop) { globalProperties_.withLoop(loop); }
|
||||
|
||||
// ------ 时间信息 ------
|
||||
float getTotalDuration() const {
|
||||
float total = 0.0f;
|
||||
for (const auto &frame : frames_) {
|
||||
total += frame.delay;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
// ------ 预计算最大帧尺寸 ------
|
||||
Size getMaxFrameSize() const {
|
||||
Size maxSize;
|
||||
for (const auto &frame : frames_) {
|
||||
if (frame.spriteFrame && frame.spriteFrame->isValid()) {
|
||||
const auto &rect = frame.spriteFrame->getRect();
|
||||
if (rect.size.width > maxSize.width)
|
||||
maxSize.width = rect.size.width;
|
||||
if (rect.size.height > maxSize.height)
|
||||
maxSize.height = rect.size.height;
|
||||
}
|
||||
}
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
// ------ 元数据 ------
|
||||
void setName(const std::string &name) { name_ = name; }
|
||||
const std::string &getName() const { return name_; }
|
||||
|
||||
void setSourcePath(const std::string &path) { sourcePath_ = path; }
|
||||
const std::string &getSourcePath() const { return sourcePath_; }
|
||||
|
||||
// ------ 静态工厂 ------
|
||||
static Ptr<AnimationClip> create(const std::string &name = "") {
|
||||
return makePtr<AnimationClip>(name);
|
||||
}
|
||||
|
||||
/// 从精灵图网格创建(所有帧按顺序)
|
||||
static Ptr<AnimationClip> createFromGrid(Ptr<Texture> texture, int frameWidth,
|
||||
int frameHeight,
|
||||
float frameDurationMs = 100.0f,
|
||||
int frameCount = -1, int spacing = 0,
|
||||
int margin = 0) {
|
||||
if (!texture)
|
||||
return nullptr;
|
||||
|
||||
int texW = texture->getWidth();
|
||||
int texH = texture->getHeight();
|
||||
int usableW = texW - 2 * margin;
|
||||
int usableH = texH - 2 * margin;
|
||||
int cols = (usableW + spacing) / (frameWidth + spacing);
|
||||
int rows = (usableH + spacing) / (frameHeight + spacing);
|
||||
int total = (frameCount > 0) ? frameCount : cols * rows;
|
||||
|
||||
auto clip = makePtr<AnimationClip>();
|
||||
for (int i = 0; i < total; ++i) {
|
||||
int col = i % cols;
|
||||
int row = i / cols;
|
||||
if (row >= rows)
|
||||
break;
|
||||
|
||||
// 翻转行顺序:精灵图第0行在顶部,但OpenGL纹理V坐标从底部开始
|
||||
// 所以将行索引翻转,使第0行对应纹理底部(V=1.0),第3行对应纹理顶部(V=0.0)
|
||||
int flippedRow = (rows - 1) - row;
|
||||
|
||||
Rect rect(
|
||||
static_cast<float>(margin + col * (frameWidth + spacing)),
|
||||
static_cast<float>(margin + flippedRow * (frameHeight + spacing)),
|
||||
static_cast<float>(frameWidth), static_cast<float>(frameHeight));
|
||||
|
||||
auto sf = SpriteFrame::create(texture, rect);
|
||||
AnimationFrame frame;
|
||||
frame.spriteFrame = std::move(sf);
|
||||
frame.delay = frameDurationMs;
|
||||
clip->addFrame(std::move(frame));
|
||||
}
|
||||
return clip;
|
||||
}
|
||||
|
||||
/// 从精灵图网格创建(指定帧索引列表)
|
||||
static Ptr<AnimationClip>
|
||||
createFromGridIndices(Ptr<Texture> texture, int frameWidth, int frameHeight,
|
||||
const std::vector<int> &frameIndices,
|
||||
float frameDurationMs = 100.0f, int spacing = 0,
|
||||
int margin = 0) {
|
||||
if (!texture)
|
||||
return nullptr;
|
||||
|
||||
int texW = texture->getWidth();
|
||||
int texH = texture->getHeight();
|
||||
int usableW = texW - 2 * margin;
|
||||
int usableH = texH - 2 * margin;
|
||||
int cols = (usableW + spacing) / (frameWidth + spacing);
|
||||
int rows = (usableH + spacing) / (frameHeight + spacing);
|
||||
|
||||
auto clip = makePtr<AnimationClip>();
|
||||
for (int idx : frameIndices) {
|
||||
int col = idx % cols;
|
||||
int row = idx / cols;
|
||||
|
||||
// 翻转行顺序:精灵图第0行在顶部,但OpenGL纹理V坐标从底部开始
|
||||
int flippedRow = (rows - 1) - row;
|
||||
|
||||
Rect rect(
|
||||
static_cast<float>(margin + col * (frameWidth + spacing)),
|
||||
static_cast<float>(margin + flippedRow * (frameHeight + spacing)),
|
||||
static_cast<float>(frameWidth), static_cast<float>(frameHeight));
|
||||
|
||||
auto sf = SpriteFrame::create(texture, rect);
|
||||
AnimationFrame frame;
|
||||
frame.spriteFrame = std::move(sf);
|
||||
frame.delay = frameDurationMs;
|
||||
clip->addFrame(std::move(frame));
|
||||
}
|
||||
return clip;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::string sourcePath_;
|
||||
std::vector<AnimationFrame> frames_;
|
||||
FramePropertySet globalProperties_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/animation/animation_clip.h>
|
||||
#include <extra2d/animation/interpolation_engine.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 动画播放状态
|
||||
// ============================================================================
|
||||
enum class AnimPlayState : uint8 { Stopped, Playing, Paused };
|
||||
|
||||
// ============================================================================
|
||||
// AnimationController - 动画播放控制器
|
||||
// 借鉴 Cocos Creator 的 AnimationState:纯播放逻辑,不持有渲染资源
|
||||
// ============================================================================
|
||||
class AnimationController {
|
||||
public:
|
||||
// 回调类型定义
|
||||
using FrameChangeCallback = std::function<void(size_t oldIdx, size_t newIdx,
|
||||
const AnimationFrame &frame)>;
|
||||
using KeyframeCallback = std::function<void(int flagIndex)>;
|
||||
using SoundTriggerCallback = std::function<void(const std::string &path)>;
|
||||
using CompletionCallback = std::function<void()>;
|
||||
|
||||
AnimationController() = default;
|
||||
|
||||
// ------ 绑定动画数据 ------
|
||||
void setClip(Ptr<AnimationClip> clip);
|
||||
Ptr<AnimationClip> getClip() const { return clip_; }
|
||||
|
||||
// ------ 播放控制 ------
|
||||
void play();
|
||||
void pause();
|
||||
void resume();
|
||||
void stop();
|
||||
void reset();
|
||||
|
||||
// ------ 帧控制 ------
|
||||
void setFrameIndex(size_t index);
|
||||
void nextFrame();
|
||||
void prevFrame();
|
||||
|
||||
// ------ 核心更新(每帧调用)------
|
||||
void update(float dt);
|
||||
|
||||
// ------ 状态查询 ------
|
||||
AnimPlayState getState() const { return state_; }
|
||||
bool isPlaying() const { return state_ == AnimPlayState::Playing; }
|
||||
bool isPaused() const { return state_ == AnimPlayState::Paused; }
|
||||
bool isStopped() const { return state_ == AnimPlayState::Stopped; }
|
||||
|
||||
size_t getCurrentFrameIndex() const { return currentFrameIndex_; }
|
||||
size_t getTotalFrames() const;
|
||||
const AnimationFrame &getCurrentFrame() const;
|
||||
|
||||
float getPlaybackSpeed() const { return playbackSpeed_; }
|
||||
void setPlaybackSpeed(float speed) { playbackSpeed_ = speed; }
|
||||
|
||||
bool isLooping() const;
|
||||
void setLooping(bool loop);
|
||||
|
||||
// ------ 插值状态 ------
|
||||
float getInterpolationFactor() const { return interpolationFactor_; }
|
||||
bool isInterpolating() const { return interpolating_; }
|
||||
|
||||
// ------ 回调注册 ------
|
||||
void setFrameChangeCallback(FrameChangeCallback cb) {
|
||||
onFrameChange_ = std::move(cb);
|
||||
}
|
||||
void setKeyframeCallback(KeyframeCallback cb) { onKeyframe_ = std::move(cb); }
|
||||
void setSoundTriggerCallback(SoundTriggerCallback cb) {
|
||||
onSoundTrigger_ = std::move(cb);
|
||||
}
|
||||
void setCompletionCallback(CompletionCallback cb) {
|
||||
onComplete_ = std::move(cb);
|
||||
}
|
||||
|
||||
private:
|
||||
Ptr<AnimationClip> clip_;
|
||||
AnimPlayState state_ = AnimPlayState::Stopped;
|
||||
|
||||
size_t currentFrameIndex_ = 0;
|
||||
float accumulatedTime_ = 0.0f; // 当前帧已累积时间 (ms)
|
||||
float playbackSpeed_ = 1.0f;
|
||||
bool loopOverride_ = false; // 外部循环覆盖值
|
||||
bool hasLoopOverride_ = false; // 是否使用外部循环覆盖
|
||||
|
||||
// 插值状态
|
||||
bool interpolating_ = false;
|
||||
float interpolationFactor_ = 0.0f;
|
||||
|
||||
// 回调
|
||||
FrameChangeCallback onFrameChange_;
|
||||
KeyframeCallback onKeyframe_;
|
||||
SoundTriggerCallback onSoundTrigger_;
|
||||
CompletionCallback onComplete_;
|
||||
|
||||
// 内部方法
|
||||
void advanceFrame(size_t newIndex);
|
||||
void processFrameProperties(const AnimationFrame &frame);
|
||||
void updateInterpolation();
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Node;
|
||||
|
||||
// ============================================================================
|
||||
// 动画事件类型
|
||||
// ============================================================================
|
||||
enum class AnimationEventType : uint32_t {
|
||||
FrameChanged = 0x2001, // 帧切换
|
||||
KeyframeHit = 0x2002, // 关键帧触发
|
||||
SoundTrigger = 0x2003, // 音效触发
|
||||
AnimationStart = 0x2004, // 动画开始播放
|
||||
AnimationEnd = 0x2005, // 动画播放结束
|
||||
AnimationLoop = 0x2006, // 动画循环一轮
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 动画事件数据
|
||||
// ============================================================================
|
||||
struct AnimationEvent {
|
||||
AnimationEventType type;
|
||||
size_t frameIndex = 0;
|
||||
size_t previousFrameIndex = 0;
|
||||
int keyframeFlag = -1;
|
||||
std::string soundPath;
|
||||
Node *source = nullptr;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 动画事件回调类型
|
||||
// ============================================================================
|
||||
using AnimationEventCallback = std::function<void(const AnimationEvent &)>;
|
||||
using KeyframeHitCallback = std::function<void(int flagIndex)>;
|
||||
using AnimationCompleteCallback = std::function<void()>;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <extra2d/animation/frame_property.h>
|
||||
#include <extra2d/animation/sprite_frame.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// AnimationFrame - 单帧数据
|
||||
// 引用 SpriteFrame 而非直接持有纹理(借鉴 Cocos 模式)
|
||||
// 通过 FramePropertySet 支持不固定数据(ANI Flag 系统增强版)
|
||||
// ============================================================================
|
||||
struct AnimationFrame {
|
||||
// ------ 核心数据(固定部分)------
|
||||
Ptr<SpriteFrame> spriteFrame; // 精灵帧引用(Cocos 模式)
|
||||
std::string texturePath; // 原始图片路径(用于解析时定位资源)
|
||||
int textureIndex = 0; // 精灵图集索引
|
||||
Vec2 offset; // 位置偏移
|
||||
float delay = 100.0f; // 帧延迟(毫秒)
|
||||
|
||||
// ------ 碰撞盒数据(DNF ANI 格式)------
|
||||
std::vector<std::array<int32_t, 6>> damageBoxes; // 伤害碰撞盒
|
||||
std::vector<std::array<int32_t, 6>> attackBoxes; // 攻击碰撞盒
|
||||
|
||||
// ------ 不固定数据(属性集合)------
|
||||
FramePropertySet properties; // 类型安全的 Flag 系统
|
||||
|
||||
// ------ 便捷方法 ------
|
||||
bool hasTexture() const {
|
||||
return spriteFrame != nullptr && spriteFrame->isValid();
|
||||
}
|
||||
|
||||
bool hasInterpolation() const {
|
||||
return properties.getOr<bool>(FramePropertyKey::Interpolation, false);
|
||||
}
|
||||
|
||||
bool hasKeyframeCallback() const {
|
||||
return properties.has(FramePropertyKey::SetFlag);
|
||||
}
|
||||
|
||||
int getKeyframeIndex() const {
|
||||
return properties.getOr<int>(FramePropertyKey::SetFlag, -1);
|
||||
}
|
||||
|
||||
Vec2 getEffectiveScale() const {
|
||||
return properties.getOr<Vec2>(FramePropertyKey::ImageRate, Vec2::One());
|
||||
}
|
||||
|
||||
float getEffectiveRotation() const {
|
||||
return properties.getOr<float>(FramePropertyKey::ImageRotate, 0.0f);
|
||||
}
|
||||
|
||||
Color getEffectiveColor() const {
|
||||
return properties.getOr<Color>(FramePropertyKey::ColorTint, Colors::White);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/animation/animation_cache.h>
|
||||
#include <extra2d/animation/animation_clip.h>
|
||||
#include <extra2d/animation/animation_controller.h>
|
||||
#include <extra2d/animation/animation_event.h>
|
||||
#include <extra2d/animation/frame_renderer.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// AnimationNode - 动画节点(继承 Node)
|
||||
// 使用 FrameRenderer 单渲染器策略,不依赖 Sprite 基类
|
||||
// 适用于需要独立渲染控制的动画(如特效、复合动画图层)
|
||||
// ============================================================================
|
||||
class AnimationNode : public Node {
|
||||
public:
|
||||
AnimationNode();
|
||||
~AnimationNode() override = default;
|
||||
|
||||
// ------ 静态工厂(Cocos 风格)------
|
||||
static Ptr<AnimationNode> create();
|
||||
static Ptr<AnimationNode> create(Ptr<AnimationClip> clip);
|
||||
static Ptr<AnimationNode> create(const std::string &aniFilePath);
|
||||
|
||||
// ------ 动画数据 ------
|
||||
void setClip(Ptr<AnimationClip> clip);
|
||||
Ptr<AnimationClip> getClip() const;
|
||||
bool loadFromFile(const std::string &aniFilePath);
|
||||
|
||||
// ------ 播放控制 ------
|
||||
void play();
|
||||
void pause();
|
||||
void resume();
|
||||
void stop();
|
||||
void reset();
|
||||
|
||||
bool isPlaying() const;
|
||||
bool isPaused() const;
|
||||
bool isStopped() const;
|
||||
|
||||
void setPlaybackSpeed(float speed);
|
||||
float getPlaybackSpeed() const;
|
||||
void setLooping(bool loop);
|
||||
bool isLooping() const;
|
||||
|
||||
// ------ 帧控制 ------
|
||||
void setFrameIndex(size_t index);
|
||||
size_t getCurrentFrameIndex() const;
|
||||
size_t getTotalFrames() const;
|
||||
|
||||
// ------ 事件回调 ------
|
||||
void setKeyframeCallback(KeyframeHitCallback callback);
|
||||
void setCompletionCallback(AnimationCompleteCallback callback);
|
||||
void
|
||||
setFrameChangeCallback(AnimationController::FrameChangeCallback callback);
|
||||
void addEventListener(AnimationEventCallback callback);
|
||||
|
||||
// ------ 视觉属性 ------
|
||||
void setTintColor(const Color &color);
|
||||
Color getTintColor() const { return tintColor_; }
|
||||
void setFlipX(bool flip) { flipX_ = flip; }
|
||||
void setFlipY(bool flip) { flipY_ = flip; }
|
||||
bool isFlipX() const { return flipX_; }
|
||||
bool isFlipY() const { return flipY_; }
|
||||
|
||||
// ------ 自动播放 ------
|
||||
void setAutoPlay(bool autoPlay) { autoPlay_ = autoPlay; }
|
||||
bool isAutoPlay() const { return autoPlay_; }
|
||||
|
||||
// ------ 碰撞盒访问 ------
|
||||
const std::vector<std::array<int32_t, 6>> &getCurrentDamageBoxes() const;
|
||||
const std::vector<std::array<int32_t, 6>> &getCurrentAttackBoxes() const;
|
||||
|
||||
// ------ 查询 ------
|
||||
Size getMaxFrameSize() const;
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
// ------ 直接访问 ------
|
||||
AnimationController &getController() { return controller_; }
|
||||
const AnimationController &getController() const { return controller_; }
|
||||
FrameRenderer &getFrameRenderer() { return frameRenderer_; }
|
||||
const FrameRenderer &getFrameRenderer() const { return frameRenderer_; }
|
||||
|
||||
protected:
|
||||
void onUpdate(float dt) override;
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
|
||||
private:
|
||||
AnimationController controller_;
|
||||
FrameRenderer frameRenderer_;
|
||||
Color tintColor_ = Colors::White;
|
||||
bool flipX_ = false;
|
||||
bool flipY_ = false;
|
||||
bool autoPlay_ = false;
|
||||
std::vector<AnimationEventCallback> eventListeners_;
|
||||
|
||||
static const std::vector<std::array<int32_t, 6>> emptyBoxes_;
|
||||
|
||||
void setupControllerCallbacks();
|
||||
void dispatchEvent(const AnimationEvent &event);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/animation/als_parser.h>
|
||||
#include <extra2d/animation/animation_event.h>
|
||||
#include <extra2d/animation/animation_node.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// CompositeAnimation - ALS 多层复合动画节点
|
||||
// 管理多个 AnimationNode 图层,统一控制播放
|
||||
// 对应 DNF 的 ALS 格式(多层动画叠加)
|
||||
// ============================================================================
|
||||
class CompositeAnimation : public Node {
|
||||
public:
|
||||
CompositeAnimation() = default;
|
||||
~CompositeAnimation() override = default;
|
||||
|
||||
// ------ 静态工厂 ------
|
||||
static Ptr<CompositeAnimation> create();
|
||||
static Ptr<CompositeAnimation> create(const std::string &alsFilePath);
|
||||
|
||||
// ------ 加载 ------
|
||||
bool loadFromFile(const std::string &alsFilePath);
|
||||
|
||||
// ------ 图层管理 ------
|
||||
void addLayer(Ptr<AnimationNode> node, int zOrder = 0);
|
||||
void removeLayer(size_t index);
|
||||
Ptr<AnimationNode> getLayer(size_t index) const;
|
||||
Ptr<AnimationNode> getMainLayer() const;
|
||||
size_t getLayerCount() const;
|
||||
|
||||
// ------ 统一播放控制 ------
|
||||
void play();
|
||||
void pause();
|
||||
void resume();
|
||||
void stop();
|
||||
void reset();
|
||||
void setPlaybackSpeed(float speed);
|
||||
void setLooping(bool loop);
|
||||
|
||||
bool isPlaying() const;
|
||||
bool isStopped() const;
|
||||
|
||||
// ------ 事件回调(绑定到主图层)------
|
||||
void setKeyframeCallback(KeyframeHitCallback callback);
|
||||
void setCompletionCallback(AnimationCompleteCallback callback);
|
||||
void addEventListener(AnimationEventCallback callback);
|
||||
|
||||
// ------ 视觉属性(应用到所有图层)------
|
||||
void setTintColor(const Color &color);
|
||||
void setFlipX(bool flip);
|
||||
void setFlipY(bool flip);
|
||||
|
||||
private:
|
||||
struct LayerEntry {
|
||||
Ptr<AnimationNode> node;
|
||||
int zOrder = 0;
|
||||
};
|
||||
std::vector<LayerEntry> layers_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,210 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <any>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 帧属性键 - 强类型枚举替代原始 ANI 的字符串键
|
||||
// ============================================================================
|
||||
enum class FramePropertyKey : uint32 {
|
||||
// 事件触发
|
||||
SetFlag = 0x0001, // int: 关键帧回调索引
|
||||
PlaySound = 0x0002, // string: 音效路径
|
||||
|
||||
// 变换属性
|
||||
ImageRate = 0x0010, // Vec2: 缩放比例
|
||||
ImageRotate = 0x0011, // float: 旋转角度(度)
|
||||
ImageOffset = 0x0012, // Vec2: 额外位置偏移
|
||||
|
||||
// 视觉效果
|
||||
BlendLinearDodge = 0x0020, // bool: 线性减淡
|
||||
BlendAdditive = 0x0021, // bool: 加法混合
|
||||
ColorTint = 0x0022, // Color: RGBA 颜色
|
||||
|
||||
// 控制标记
|
||||
Interpolation = 0x0030, // bool: 启用到下一帧的插值
|
||||
Loop = 0x0031, // bool: 全局循环标记
|
||||
|
||||
// DNF ANI 扩展属性
|
||||
DamageType = 0x0040, // int: 伤害类型 (0=Normal, 1=SuperArmor, 2=Unbreakable)
|
||||
Shadow = 0x0041, // bool: 阴影
|
||||
FlipType = 0x0042, // int: 翻转类型 (1=Horizon, 2=Vertical, 3=All)
|
||||
Coord = 0x0043, // int: 坐标系
|
||||
LoopStart = 0x0044, // bool: 循环起始标记
|
||||
LoopEnd = 0x0045, // int: 循环结束帧数
|
||||
GraphicEffect = 0x0046, // int: 图形特效类型
|
||||
ClipRegion = 0x0047, // vector<int>: 裁剪区域 [4个int16]
|
||||
|
||||
// 用户自定义扩展区间 (0x1000+)
|
||||
UserDefined = 0x1000,
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 帧属性值 - variant 多态值(优化版本)
|
||||
// 使用紧凑存储,常用小类型直接内联,大类型使用索引引用
|
||||
// ============================================================================
|
||||
|
||||
// 前向声明
|
||||
struct FramePropertyValue;
|
||||
|
||||
// 属性存储类型枚举
|
||||
enum class PropertyValueType : uint8_t {
|
||||
Empty = 0,
|
||||
Bool = 1,
|
||||
Int = 2,
|
||||
Float = 3,
|
||||
Vec2 = 4,
|
||||
Color = 5,
|
||||
String = 6, // 字符串使用索引引用
|
||||
IntVector = 7, // vector<int> 使用索引引用
|
||||
};
|
||||
|
||||
// 紧凑的属性值结构(16字节)
|
||||
struct FramePropertyValue {
|
||||
PropertyValueType type = PropertyValueType::Empty;
|
||||
uint8_t padding[3] = {0};
|
||||
|
||||
// 使用结构体包装非平凡类型,使其可以在union中使用
|
||||
struct Vec2Storage {
|
||||
float x, y;
|
||||
Vec2Storage() = default;
|
||||
Vec2Storage(const Vec2& v) : x(v.x), y(v.y) {}
|
||||
operator Vec2() const { return Vec2(x, y); }
|
||||
};
|
||||
|
||||
struct ColorStorage {
|
||||
float r, g, b, a;
|
||||
ColorStorage() = default;
|
||||
ColorStorage(const Color& c) : r(c.r), g(c.g), b(c.b), a(c.a) {}
|
||||
operator Color() const { return Color(r, g, b, a); }
|
||||
};
|
||||
|
||||
union Data {
|
||||
bool boolValue;
|
||||
int intValue;
|
||||
float floatValue;
|
||||
Vec2Storage vec2Value;
|
||||
ColorStorage colorValue;
|
||||
uint32_t stringIndex; // 字符串池索引
|
||||
uint32_t vectorIndex; // vector池索引
|
||||
|
||||
Data() : intValue(0) {} // 默认构造函数
|
||||
~Data() {} // 析构函数
|
||||
} data;
|
||||
|
||||
FramePropertyValue() : type(PropertyValueType::Empty) {}
|
||||
explicit FramePropertyValue(bool v) : type(PropertyValueType::Bool) { data.boolValue = v; }
|
||||
explicit FramePropertyValue(int v) : type(PropertyValueType::Int) { data.intValue = v; }
|
||||
explicit FramePropertyValue(float v) : type(PropertyValueType::Float) { data.floatValue = v; }
|
||||
explicit FramePropertyValue(const Vec2& v) : type(PropertyValueType::Vec2) { data.vec2Value = v; }
|
||||
explicit FramePropertyValue(const Color& v) : type(PropertyValueType::Color) { data.colorValue = v; }
|
||||
|
||||
bool isInline() const {
|
||||
return type <= PropertyValueType::Color;
|
||||
}
|
||||
|
||||
bool isString() const { return type == PropertyValueType::String; }
|
||||
bool isIntVector() const { return type == PropertyValueType::IntVector; }
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// FramePropertyKey 的 hash 支持
|
||||
// ============================================================================
|
||||
struct FramePropertyKeyHash {
|
||||
size_t operator()(FramePropertyKey key) const noexcept {
|
||||
return std::hash<uint32>{}(static_cast<uint32>(key));
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// FramePropertySet - 单帧属性集合(优化版本)
|
||||
// 使用紧凑存储和线性探测哈希表,提高缓存命中率
|
||||
// ============================================================================
|
||||
class FramePropertySet {
|
||||
public:
|
||||
FramePropertySet() = default;
|
||||
|
||||
// ------ 设置属性 ------
|
||||
void set(FramePropertyKey key, FramePropertyValue value);
|
||||
void set(FramePropertyKey key, bool value) { set(key, FramePropertyValue(value)); }
|
||||
void set(FramePropertyKey key, int value) { set(key, FramePropertyValue(value)); }
|
||||
void set(FramePropertyKey key, float value) { set(key, FramePropertyValue(value)); }
|
||||
void set(FramePropertyKey key, const Vec2& value) { set(key, FramePropertyValue(value)); }
|
||||
void set(FramePropertyKey key, const Color& value) { set(key, FramePropertyValue(value)); }
|
||||
void set(FramePropertyKey key, const std::string& value);
|
||||
void set(FramePropertyKey key, const std::vector<int>& value);
|
||||
|
||||
void setCustom(const std::string &key, std::any value);
|
||||
|
||||
// ------ 类型安全获取 ------
|
||||
template <typename T> std::optional<T> get(FramePropertyKey key) const;
|
||||
|
||||
template <typename T>
|
||||
T getOr(FramePropertyKey key, const T &defaultValue) const {
|
||||
auto result = get<T>(key);
|
||||
return result.value_or(defaultValue);
|
||||
}
|
||||
|
||||
std::optional<std::any> getCustom(const std::string &key) const;
|
||||
|
||||
// ------ 查询 ------
|
||||
bool has(FramePropertyKey key) const;
|
||||
bool hasCustom(const std::string &key) const;
|
||||
bool empty() const { return properties_.empty() && customProperties_.empty(); }
|
||||
size_t count() const { return properties_.size() + customProperties_.size(); }
|
||||
|
||||
// ------ 移除 ------
|
||||
void remove(FramePropertyKey key);
|
||||
void removeCustom(const std::string &key);
|
||||
void clear();
|
||||
|
||||
// ------ 迭代 ------
|
||||
using PropertyMap = std::unordered_map<FramePropertyKey, FramePropertyValue,
|
||||
FramePropertyKeyHash>;
|
||||
const PropertyMap &properties() const { return properties_; }
|
||||
|
||||
// ------ 链式 API ------
|
||||
FramePropertySet &withSetFlag(int index);
|
||||
FramePropertySet &withPlaySound(const std::string &path);
|
||||
FramePropertySet &withImageRate(const Vec2 &scale);
|
||||
FramePropertySet &withImageRotate(float degrees);
|
||||
FramePropertySet &withColorTint(const Color &color);
|
||||
FramePropertySet &withInterpolation(bool enabled = true);
|
||||
FramePropertySet &withBlendLinearDodge(bool enabled = true);
|
||||
FramePropertySet &withLoop(bool enabled = true);
|
||||
|
||||
private:
|
||||
PropertyMap properties_;
|
||||
std::unordered_map<std::string, std::any> customProperties_;
|
||||
|
||||
// 字符串池和vector池,用于存储大对象
|
||||
mutable std::vector<std::string> stringPool_;
|
||||
mutable std::vector<std::vector<int>> vectorPool_;
|
||||
mutable uint32_t nextStringIndex_ = 0;
|
||||
mutable uint32_t nextVectorIndex_ = 0;
|
||||
|
||||
uint32_t allocateString(const std::string& str);
|
||||
uint32_t allocateVector(const std::vector<int>& vec);
|
||||
const std::string* getString(uint32_t index) const;
|
||||
const std::vector<int>* getVector(uint32_t index) const;
|
||||
};
|
||||
|
||||
// 模板特化声明
|
||||
template <> std::optional<bool> FramePropertySet::get<bool>(FramePropertyKey key) const;
|
||||
template <> std::optional<int> FramePropertySet::get<int>(FramePropertyKey key) const;
|
||||
template <> std::optional<float> FramePropertySet::get<float>(FramePropertyKey key) const;
|
||||
template <> std::optional<Vec2> FramePropertySet::get<Vec2>(FramePropertyKey key) const;
|
||||
template <> std::optional<Color> FramePropertySet::get<Color>(FramePropertyKey key) const;
|
||||
template <> std::optional<std::string> FramePropertySet::get<std::string>(FramePropertyKey key) const;
|
||||
template <> std::optional<std::vector<int>> FramePropertySet::get<std::vector<int>>(FramePropertyKey key) const;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/animation/animation_frame.h>
|
||||
#include <extra2d/animation/interpolation_engine.h>
|
||||
#include <extra2d/animation/sprite_frame.h>
|
||||
#include <extra2d/animation/sprite_frame_cache.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// FrameRenderer - 帧渲染器
|
||||
// 单渲染器 + SpriteFrame 引用策略,替代 N帧=N个Sprite 的旧设计
|
||||
// 负责预加载帧的 SpriteFrame、渲染当前帧、处理混合模式
|
||||
// ============================================================================
|
||||
class FrameRenderer {
|
||||
public:
|
||||
FrameRenderer() = default;
|
||||
|
||||
// ------ 预加载 ------
|
||||
// 解析所有帧的 SpriteFrame(通过 SpriteFrameCache)
|
||||
bool preloadFrames(const std::vector<AnimationFrame> &frames);
|
||||
void releaseFrames();
|
||||
|
||||
// ------ 渲染当前帧 ------
|
||||
void renderFrame(RenderBackend &renderer, const AnimationFrame &frame,
|
||||
size_t frameIndex, const Vec2 &position, float nodeOpacity,
|
||||
const Color &tintColor, bool flipX, bool flipY);
|
||||
|
||||
// ------ 渲染插值帧 ------
|
||||
void renderInterpolated(RenderBackend &renderer,
|
||||
const AnimationFrame &fromFrame, size_t fromIndex,
|
||||
const InterpolatedProperties &props,
|
||||
const Vec2 &position, float nodeOpacity,
|
||||
const Color &tintColor, bool flipX, bool flipY);
|
||||
|
||||
// ------ 混合模式映射 ------
|
||||
static BlendMode mapBlendMode(const FramePropertySet &props);
|
||||
|
||||
// ------ 查询 ------
|
||||
Ptr<SpriteFrame> getSpriteFrame(size_t frameIndex) const;
|
||||
Size getMaxFrameSize() const { return maxFrameSize_; }
|
||||
bool isLoaded() const { return !spriteFrames_.empty(); }
|
||||
|
||||
private:
|
||||
std::vector<Ptr<SpriteFrame>> spriteFrames_;
|
||||
Size maxFrameSize_;
|
||||
|
||||
void drawSpriteFrame(RenderBackend &renderer, Ptr<SpriteFrame> sf,
|
||||
const Vec2 &position, const Vec2 &offset,
|
||||
const Vec2 &scale, float rotation, float opacity,
|
||||
const Color &tint, bool flipX, bool flipY,
|
||||
BlendMode blend);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <extra2d/animation/animation_frame.h>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 插值结果 - 两帧之间的插值后属性
|
||||
// ============================================================================
|
||||
struct InterpolatedProperties {
|
||||
Vec2 position;
|
||||
Vec2 scale = Vec2::One();
|
||||
float rotation = 0.0f;
|
||||
Color color = Colors::White;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 插值曲线类型
|
||||
// ============================================================================
|
||||
enum class InterpolationCurve : uint8 {
|
||||
Linear, // 线性(原始系统的 uniform velocity)
|
||||
EaseIn, // 缓入
|
||||
EaseOut, // 缓出
|
||||
EaseInOut, // 缓入缓出
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// InterpolationEngine - 帧间属性插值计算(静态方法,无状态)
|
||||
// 独立于 AnimationController,可复用于其他系统
|
||||
// ============================================================================
|
||||
class InterpolationEngine {
|
||||
public:
|
||||
/// 核心插值计算:根据 t 因子 (0~1) 计算两帧之间的插值属性
|
||||
static InterpolatedProperties
|
||||
interpolate(const AnimationFrame &from, const AnimationFrame &to, float t,
|
||||
InterpolationCurve curve = InterpolationCurve::Linear) {
|
||||
float curvedT = applyCurve(t, curve);
|
||||
|
||||
InterpolatedProperties result;
|
||||
result.position = lerpPosition(from, to, curvedT);
|
||||
result.scale = lerpScale(from, to, curvedT);
|
||||
result.rotation = lerpRotation(from, to, curvedT);
|
||||
result.color = lerpColor(from, to, curvedT);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 位置插值
|
||||
static Vec2 lerpPosition(const AnimationFrame &from, const AnimationFrame &to,
|
||||
float t) {
|
||||
return Vec2::lerp(from.offset, to.offset, t);
|
||||
}
|
||||
|
||||
/// 缩放插值
|
||||
static Vec2 lerpScale(const AnimationFrame &from, const AnimationFrame &to,
|
||||
float t) {
|
||||
Vec2 fromScale = from.getEffectiveScale();
|
||||
Vec2 toScale = to.getEffectiveScale();
|
||||
return Vec2::lerp(fromScale, toScale, t);
|
||||
}
|
||||
|
||||
/// 旋转插值
|
||||
static float lerpRotation(const AnimationFrame &from,
|
||||
const AnimationFrame &to, float t) {
|
||||
float fromRot = from.getEffectiveRotation();
|
||||
float toRot = to.getEffectiveRotation();
|
||||
return math::lerp(fromRot, toRot, t);
|
||||
}
|
||||
|
||||
/// 颜色插值
|
||||
static Color lerpColor(const AnimationFrame &from, const AnimationFrame &to,
|
||||
float t) {
|
||||
Color fromColor = from.getEffectiveColor();
|
||||
Color toColor = to.getEffectiveColor();
|
||||
return Color::lerp(fromColor, toColor, t);
|
||||
}
|
||||
|
||||
/// 应用曲线函数
|
||||
static float applyCurve(float t, InterpolationCurve curve) {
|
||||
t = math::clamp(t, 0.0f, 1.0f);
|
||||
|
||||
switch (curve) {
|
||||
case InterpolationCurve::Linear:
|
||||
return t;
|
||||
|
||||
case InterpolationCurve::EaseIn:
|
||||
return t * t;
|
||||
|
||||
case InterpolationCurve::EaseOut:
|
||||
return t * (2.0f - t);
|
||||
|
||||
case InterpolationCurve::EaseInOut:
|
||||
if (t < 0.5f)
|
||||
return 2.0f * t * t;
|
||||
else
|
||||
return -1.0f + (4.0f - 2.0f * t) * t;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// SpriteFrame - 精灵帧(纹理 + 区域 + 偏移的中间抽象)
|
||||
// 借鉴 Cocos2d-x SpriteFrame:解耦纹理物理存储与逻辑帧
|
||||
// 一个纹理图集可包含多个 SpriteFrame,减少纹理切换提升渲染性能
|
||||
// ============================================================================
|
||||
class SpriteFrame {
|
||||
public:
|
||||
SpriteFrame() = default;
|
||||
|
||||
SpriteFrame(Ptr<Texture> texture, const Rect &rect)
|
||||
: texture_(std::move(texture)), rect_(rect), originalSize_(rect.size) {}
|
||||
|
||||
SpriteFrame(Ptr<Texture> texture, const Rect &rect, const Vec2 &offset,
|
||||
const Size &originalSize)
|
||||
: texture_(std::move(texture)), rect_(rect), offset_(offset),
|
||||
originalSize_(originalSize) {}
|
||||
|
||||
// ------ 静态创建 ------
|
||||
static Ptr<SpriteFrame> create(Ptr<Texture> texture, const Rect &rect) {
|
||||
return makePtr<SpriteFrame>(std::move(texture), rect);
|
||||
}
|
||||
|
||||
static Ptr<SpriteFrame> create(Ptr<Texture> texture, const Rect &rect,
|
||||
const Vec2 &offset, const Size &originalSize) {
|
||||
return makePtr<SpriteFrame>(std::move(texture), rect, offset, originalSize);
|
||||
}
|
||||
|
||||
// ------ 纹理信息 ------
|
||||
void setTexture(Ptr<Texture> texture) { texture_ = std::move(texture); }
|
||||
Ptr<Texture> getTexture() const { return texture_; }
|
||||
|
||||
// ------ 矩形区域(在纹理图集中的位置)------
|
||||
void setRect(const Rect &rect) { rect_ = rect; }
|
||||
const Rect &getRect() const { return rect_; }
|
||||
|
||||
// ------ 偏移(图集打包时的裁剪偏移)------
|
||||
void setOffset(const Vec2 &offset) { offset_ = offset; }
|
||||
const Vec2 &getOffset() const { return offset_; }
|
||||
|
||||
// ------ 原始尺寸(裁剪前的完整尺寸)------
|
||||
void setOriginalSize(const Size &size) { originalSize_ = size; }
|
||||
const Size &getOriginalSize() const { return originalSize_; }
|
||||
|
||||
// ------ 旋转标志(图集工具可能旋转90度)------
|
||||
void setRotated(bool rotated) { rotated_ = rotated; }
|
||||
bool isRotated() const { return rotated_; }
|
||||
|
||||
// ------ 名称(用于缓存索引)------
|
||||
void setName(const std::string &name) { name_ = name; }
|
||||
const std::string &getName() const { return name_; }
|
||||
|
||||
// ------ 有效性检查 ------
|
||||
bool isValid() const { return texture_ != nullptr; }
|
||||
|
||||
private:
|
||||
Ptr<Texture> texture_;
|
||||
Rect rect_;
|
||||
Vec2 offset_;
|
||||
Size originalSize_;
|
||||
bool rotated_ = false;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/animation/sprite_frame.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/resource/resource_manager.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// SpriteFrameCache - 精灵帧全局缓存(借鉴 Cocos SpriteFrameCache)
|
||||
// 全局单例管理所有精灵帧,避免重复创建,支持图集自动切割
|
||||
// ============================================================================
|
||||
class SpriteFrameCache {
|
||||
public:
|
||||
static SpriteFrameCache &getInstance() {
|
||||
static SpriteFrameCache instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
// ------ 添加帧 ------
|
||||
|
||||
/// 添加单个精灵帧
|
||||
void addSpriteFrame(Ptr<SpriteFrame> frame, const std::string &name) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
frames_[name] = std::move(frame);
|
||||
}
|
||||
|
||||
/// 从纹理和矩形区域创建并添加帧
|
||||
void addSpriteFrameFromTexture(Ptr<Texture> texture, const Rect &rect,
|
||||
const std::string &name) {
|
||||
auto frame = SpriteFrame::create(std::move(texture), rect);
|
||||
frame->setName(name);
|
||||
addSpriteFrame(std::move(frame), name);
|
||||
}
|
||||
|
||||
/// 从纹理图集批量切割添加(等宽等高网格)
|
||||
void addSpriteFramesFromGrid(const std::string &texturePath, int frameWidth,
|
||||
int frameHeight, int frameCount = -1,
|
||||
int spacing = 0, int margin = 0) {
|
||||
auto texture = loadTextureFromFile(texturePath);
|
||||
if (!texture)
|
||||
return;
|
||||
addSpriteFramesFromGrid(texture, texturePath, frameWidth, frameHeight,
|
||||
frameCount, spacing, margin);
|
||||
}
|
||||
|
||||
/// 从纹理对象批量切割添加(等宽等高网格,无需走 TexturePool)
|
||||
void addSpriteFramesFromGrid(Ptr<Texture> texture,
|
||||
const std::string &keyPrefix, int frameWidth,
|
||||
int frameHeight, int frameCount = -1,
|
||||
int spacing = 0, int margin = 0) {
|
||||
if (!texture)
|
||||
return;
|
||||
|
||||
int texW = texture->getWidth();
|
||||
int texH = texture->getHeight();
|
||||
int usableW = texW - 2 * margin;
|
||||
int usableH = texH - 2 * margin;
|
||||
int cols = (usableW + spacing) / (frameWidth + spacing);
|
||||
int rows = (usableH + spacing) / (frameHeight + spacing);
|
||||
int total = (frameCount > 0) ? frameCount : cols * rows;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (int i = 0; i < total; ++i) {
|
||||
int col = i % cols;
|
||||
int row = i / cols;
|
||||
if (row >= rows)
|
||||
break;
|
||||
|
||||
Rect rect(static_cast<float>(margin + col * (frameWidth + spacing)),
|
||||
static_cast<float>(margin + row * (frameHeight + spacing)),
|
||||
static_cast<float>(frameWidth),
|
||||
static_cast<float>(frameHeight));
|
||||
|
||||
std::string name = keyPrefix + "#" + std::to_string(i);
|
||||
auto frame = SpriteFrame::create(texture, rect);
|
||||
frame->setName(name);
|
||||
frames_[name] = std::move(frame);
|
||||
}
|
||||
}
|
||||
|
||||
// ------ 获取帧 ------
|
||||
|
||||
/// 按名称获取
|
||||
Ptr<SpriteFrame> getSpriteFrame(const std::string &name) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto it = frames_.find(name);
|
||||
if (it != frames_.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// 通过路径+索引获取或创建(ANI 格式的定位方式)
|
||||
Ptr<SpriteFrame> getOrCreateFromFile(const std::string &texturePath,
|
||||
int index = 0) {
|
||||
std::string key = texturePath + "#" + std::to_string(index);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto it = frames_.find(key);
|
||||
if (it != frames_.end())
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// 缓存未命中,加载纹理并创建 SpriteFrame
|
||||
auto texture = loadTextureFromFile(texturePath);
|
||||
if (!texture)
|
||||
return nullptr;
|
||||
|
||||
// 默认整张纹理作为一帧(index=0),或用整张纹理
|
||||
Rect rect(0.0f, 0.0f, static_cast<float>(texture->getWidth()),
|
||||
static_cast<float>(texture->getHeight()));
|
||||
|
||||
auto frame = SpriteFrame::create(texture, rect);
|
||||
frame->setName(key);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
frames_[key] = frame;
|
||||
return frame;
|
||||
}
|
||||
|
||||
// ------ 缓存管理 ------
|
||||
|
||||
bool has(const std::string &name) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return frames_.find(name) != frames_.end();
|
||||
}
|
||||
|
||||
void removeSpriteFrame(const std::string &name) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
frames_.erase(name);
|
||||
}
|
||||
|
||||
/// 移除未被外部引用的精灵帧(use_count == 1 表示仅缓存自身持有)
|
||||
void removeUnusedSpriteFrames() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (auto it = frames_.begin(); it != frames_.end();) {
|
||||
if (it->second.use_count() == 1) {
|
||||
it = frames_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
frames_.clear();
|
||||
}
|
||||
|
||||
size_t count() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return frames_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
SpriteFrameCache() = default;
|
||||
~SpriteFrameCache() = default;
|
||||
SpriteFrameCache(const SpriteFrameCache &) = delete;
|
||||
SpriteFrameCache &operator=(const SpriteFrameCache &) = delete;
|
||||
|
||||
/**
|
||||
* @brief 从文件加载纹理(使用 ResourceManager 的 LRU 缓存)
|
||||
* @param filepath 纹理文件路径
|
||||
* @return 纹理对象,失败返回nullptr
|
||||
*/
|
||||
Ptr<Texture> loadTextureFromFile(const std::string &filepath) {
|
||||
// 使用 ResourceManager 的纹理缓存机制
|
||||
// 这样可以享受 LRU 缓存、自动清理和缓存统计等功能
|
||||
auto &resources = ResourceManager::getInstance();
|
||||
|
||||
// 先检查缓存中是否已有该纹理
|
||||
auto texture = resources.getTexture(filepath);
|
||||
if (texture) {
|
||||
E2D_TRACE("SpriteFrameCache: 使用缓存纹理: {}", filepath);
|
||||
return texture;
|
||||
}
|
||||
|
||||
// 缓存未命中,通过 ResourceManager 加载
|
||||
texture = resources.loadTexture(filepath);
|
||||
if (!texture) {
|
||||
E2D_ERROR("SpriteFrameCache: 加载纹理失败: {}", filepath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
E2D_TRACE("SpriteFrameCache: 加载新纹理: {}", filepath);
|
||||
return texture;
|
||||
}
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
std::unordered_map<std::string, Ptr<SpriteFrame>> frames_;
|
||||
};
|
||||
|
||||
// 便捷宏
|
||||
#define E2D_SPRITE_FRAME_CACHE() ::extra2d::SpriteFrameCache::getInstance()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,133 +1,130 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/string.h>
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/core/registry.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/platform/window.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Input;
|
||||
class AudioEngine;
|
||||
class SceneManager;
|
||||
class ResourceManager;
|
||||
class TimerManager;
|
||||
class EventQueue;
|
||||
class EventDispatcher;
|
||||
class Camera;
|
||||
class IWindow;
|
||||
class IInput;
|
||||
class RenderBackend;
|
||||
class WindowModule;
|
||||
class RenderModule;
|
||||
class InputModule;
|
||||
|
||||
// ============================================================================
|
||||
// Application 配置
|
||||
// ============================================================================
|
||||
|
||||
enum class PlatformType {
|
||||
Auto = 0,
|
||||
PC,
|
||||
Switch
|
||||
};
|
||||
|
||||
struct AppConfig {
|
||||
std::string title = "Easy2D Application";
|
||||
int width = 800;
|
||||
int height = 600;
|
||||
bool fullscreen = false;
|
||||
bool resizable = true;
|
||||
bool vsync = true;
|
||||
int fpsLimit = 0;
|
||||
BackendType renderBackend = BackendType::OpenGL;
|
||||
int msaaSamples = 0;
|
||||
PlatformType platform = PlatformType::Auto;
|
||||
// 窗口高级配置
|
||||
bool enableCursors = true; // 是否启用光标
|
||||
bool enableDpiScale = false; // 是否启用DPI缩放
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Application 单例 - 应用主控
|
||||
// ============================================================================
|
||||
/**
|
||||
* @brief 应用程序类
|
||||
*/
|
||||
class Application {
|
||||
public:
|
||||
// Meyer's 单例
|
||||
static Application &instance();
|
||||
static Application &get();
|
||||
|
||||
// 禁止拷贝
|
||||
Application(const Application &) = delete;
|
||||
Application &operator=(const Application &) = delete;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 生命周期
|
||||
// ------------------------------------------------------------------------
|
||||
bool init(const AppConfig &config);
|
||||
/**
|
||||
* @brief 应用信息
|
||||
*/
|
||||
std::string appName = "Extra2D App";
|
||||
std::string appVersion = "1.0.0";
|
||||
std::string organization = "";
|
||||
|
||||
/**
|
||||
* @brief 注册模块
|
||||
* @tparam T 模块类型
|
||||
* @tparam Args 构造函数参数
|
||||
* @return 模块指针
|
||||
*/
|
||||
template <typename T, typename... Args> T *use(Args &&...args) {
|
||||
return Registry::instance().use<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取模块
|
||||
* @tparam T 模块类型
|
||||
* @return 模块指针
|
||||
*/
|
||||
template <typename T> T *get() const { return Registry::instance().get<T>(); }
|
||||
|
||||
/**
|
||||
* @brief 初始化
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief 关闭
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 运行主循环
|
||||
*/
|
||||
void run();
|
||||
|
||||
/**
|
||||
* @brief 请求退出
|
||||
*/
|
||||
void quit();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 状态控制
|
||||
// ------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief 暂停
|
||||
*/
|
||||
void pause();
|
||||
|
||||
/**
|
||||
* @brief 恢复
|
||||
*/
|
||||
void resume();
|
||||
|
||||
bool isPaused() const { return paused_; }
|
||||
bool isRunning() const { return running_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 子系统访问
|
||||
// ------------------------------------------------------------------------
|
||||
Window &window() { return *window_; }
|
||||
RenderBackend &renderer() { return *renderer_; }
|
||||
Input &input();
|
||||
AudioEngine &audio();
|
||||
SceneManager &scenes();
|
||||
ResourceManager &resources();
|
||||
TimerManager &timers();
|
||||
EventQueue &eventQueue();
|
||||
EventDispatcher &eventDispatcher();
|
||||
Camera &camera();
|
||||
/**
|
||||
* @brief 获取窗口
|
||||
* @return 窗口指针
|
||||
*/
|
||||
IWindow *window();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 便捷方法
|
||||
// ------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief 获取渲染器
|
||||
* @return 渲染器指针
|
||||
*/
|
||||
RenderBackend *renderer();
|
||||
|
||||
/**
|
||||
* @brief 获取输入
|
||||
* @return 输入指针
|
||||
*/
|
||||
IInput *input();
|
||||
|
||||
/**
|
||||
* @brief 进入场景
|
||||
* @param scene 场景指针
|
||||
*/
|
||||
void enterScene(Ptr<class Scene> scene);
|
||||
void enterScene(Ptr<class Scene> scene, Ptr<class TransitionScene> transitionScene);
|
||||
|
||||
float deltaTime() const { return deltaTime_; }
|
||||
float totalTime() const { return totalTime_; }
|
||||
int fps() const { return currentFps_; }
|
||||
|
||||
// 获取配置
|
||||
const AppConfig &getConfig() const { return config_; }
|
||||
|
||||
private:
|
||||
Application() = default;
|
||||
Application();
|
||||
~Application();
|
||||
|
||||
void mainLoop();
|
||||
void update();
|
||||
void render();
|
||||
void prewarmObjectPools();
|
||||
void configureCameraService();
|
||||
|
||||
// 配置
|
||||
AppConfig config_;
|
||||
|
||||
// 子系统
|
||||
UniquePtr<Window> window_;
|
||||
UniquePtr<RenderBackend> renderer_;
|
||||
UniquePtr<SceneManager> sceneManager_;
|
||||
UniquePtr<ResourceManager> resourceManager_;
|
||||
UniquePtr<TimerManager> timerManager_;
|
||||
UniquePtr<EventQueue> eventQueue_;
|
||||
UniquePtr<EventDispatcher> eventDispatcher_;
|
||||
UniquePtr<Camera> camera_;
|
||||
|
||||
// 状态
|
||||
bool initialized_ = false;
|
||||
bool running_ = false;
|
||||
bool paused_ = false;
|
||||
bool shouldQuit_ = false;
|
||||
|
||||
// 时间
|
||||
float deltaTime_ = 0.0f;
|
||||
float totalTime_ = 0.0f;
|
||||
double lastFrameTime_ = 0.0;
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <extra2d/core/types.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Sound;
|
||||
|
||||
class AudioEngine {
|
||||
public:
|
||||
static AudioEngine& getInstance();
|
||||
|
||||
AudioEngine(const AudioEngine&) = delete;
|
||||
AudioEngine& operator=(const AudioEngine&) = delete;
|
||||
AudioEngine(AudioEngine&&) = delete;
|
||||
AudioEngine& operator=(AudioEngine&&) = delete;
|
||||
|
||||
bool initialize();
|
||||
void shutdown();
|
||||
|
||||
std::shared_ptr<Sound> loadSound(const std::string& filePath);
|
||||
std::shared_ptr<Sound> loadSound(const std::string& name, const std::string& filePath);
|
||||
|
||||
std::shared_ptr<Sound> getSound(const std::string& name);
|
||||
void unloadSound(const std::string& name);
|
||||
void unloadAllSounds();
|
||||
|
||||
void setMasterVolume(float volume);
|
||||
float getMasterVolume() const;
|
||||
|
||||
void pauseAll();
|
||||
void resumeAll();
|
||||
void stopAll();
|
||||
|
||||
private:
|
||||
AudioEngine() = default;
|
||||
~AudioEngine();
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<Sound>> sounds_;
|
||||
float masterVolume_ = 1.0f;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <extra2d/core/types.h>
|
||||
|
||||
struct Mix_Chunk;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class AudioEngine;
|
||||
|
||||
class Sound {
|
||||
public:
|
||||
~Sound();
|
||||
|
||||
Sound(const Sound&) = delete;
|
||||
Sound& operator=(const Sound&) = delete;
|
||||
|
||||
bool play();
|
||||
void pause();
|
||||
void resume();
|
||||
void stop();
|
||||
|
||||
bool isPlaying() const;
|
||||
bool isPaused() const;
|
||||
|
||||
void setVolume(float volume);
|
||||
float getVolume() const { return volume_; }
|
||||
|
||||
void setLooping(bool looping);
|
||||
bool isLooping() const { return looping_; }
|
||||
|
||||
void setPitch(float pitch);
|
||||
float getPitch() const { return pitch_; }
|
||||
|
||||
float getDuration() const;
|
||||
float getCursor() const;
|
||||
void setCursor(float seconds);
|
||||
|
||||
const std::string& getFilePath() const { return filePath_; }
|
||||
const std::string& getName() const { return name_; }
|
||||
|
||||
private:
|
||||
friend class AudioEngine;
|
||||
|
||||
Sound(const std::string& name, const std::string& filePath, Mix_Chunk* chunk);
|
||||
|
||||
std::string name_;
|
||||
std::string filePath_;
|
||||
Mix_Chunk* chunk_ = nullptr;
|
||||
int channel_ = -1; // SDL_mixer 分配的通道,-1 表示未播放
|
||||
float volume_ = 1.0f;
|
||||
float pitch_ = 1.0f;
|
||||
bool looping_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -155,17 +155,4 @@ inline constexpr Color Coral{1.0f, 0.498f, 0.314f, 1.0f};
|
|||
inline constexpr Color Transparent{0.0f, 0.0f, 0.0f, 0.0f};
|
||||
} // namespace Colors
|
||||
|
||||
// 为了向后兼容,在 Color 结构体内提供静态引用
|
||||
struct ColorConstants {
|
||||
static const Color &White;
|
||||
static const Color &Black;
|
||||
static const Color &Red;
|
||||
static const Color &Green;
|
||||
static const Color &Blue;
|
||||
static const Color &Yellow;
|
||||
static const Color &Cyan;
|
||||
static const Color &Magenta;
|
||||
static const Color &Transparent;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -330,6 +330,221 @@ inline float degrees(float radians) { return radians * RAD_TO_DEG; }
|
|||
|
||||
inline float radians(float degrees) { return degrees * DEG_TO_RAD; }
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 角度工具函数
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 规范化角度到 [0, 360) 范围
|
||||
* @param degrees 输入角度(度数)
|
||||
* @return 规范化后的角度,范围 [0, 360)
|
||||
*/
|
||||
inline float normalizeAngle360(float degrees) {
|
||||
degrees = std::fmod(degrees, 360.0f);
|
||||
if (degrees < 0.0f) {
|
||||
degrees += 360.0f;
|
||||
}
|
||||
return degrees;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 规范化角度到 [-180, 180) 范围
|
||||
* @param degrees 输入角度(度数)
|
||||
* @return 规范化后的角度,范围 [-180, 180)
|
||||
*/
|
||||
inline float normalizeAngle180(float degrees) {
|
||||
degrees = std::fmod(degrees + 180.0f, 360.0f);
|
||||
if (degrees < 0.0f) {
|
||||
degrees += 360.0f;
|
||||
}
|
||||
return degrees - 180.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算两个角度之间的最短差值
|
||||
* @param from 起始角度(度数)
|
||||
* @param to 目标角度(度数)
|
||||
* @return 从 from 到 to 的最短角度差,范围 [-180, 180]
|
||||
*/
|
||||
inline float angleDifference(float from, float to) {
|
||||
float diff = normalizeAngle360(to - from);
|
||||
if (diff > 180.0f) {
|
||||
diff -= 360.0f;
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 线性插值角度
|
||||
* @param from 起始角度(度数)
|
||||
* @param to 目标角度(度数)
|
||||
* @param t 插值因子 [0, 1]
|
||||
* @return 插值后的角度
|
||||
*/
|
||||
inline float lerpAngle(float from, float to, float t) {
|
||||
return from + angleDifference(from, to) * t;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 向量工具函数
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 计算方向向量(从 from 指向 to 的单位向量)
|
||||
* @param from 起始点
|
||||
* @param to 目标点
|
||||
* @return 归一化的方向向量
|
||||
*/
|
||||
inline Vec2 direction(const Vec2 &from, const Vec2 &to) {
|
||||
return (to - from).normalized();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算两点之间的角度
|
||||
* @param from 起始点
|
||||
* @param to 目标点
|
||||
* @return 角度(度数),范围 [-180, 180]
|
||||
*/
|
||||
inline float angleBetween(const Vec2 &from, const Vec2 &to) {
|
||||
Vec2 dir = to - from;
|
||||
return std::atan2(dir.y, dir.x) * RAD_TO_DEG;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据角度创建方向向量
|
||||
* @param degrees 角度(度数),0度指向右方,逆时针为正
|
||||
* @return 单位方向向量
|
||||
*/
|
||||
inline Vec2 angleToVector(float degrees) {
|
||||
float rad = degrees * DEG_TO_RAD;
|
||||
return {std::cos(rad), std::sin(rad)};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将向量旋转指定角度
|
||||
* @param v 原始向量
|
||||
* @param degrees 旋转角度(度数),正值为逆时针旋转
|
||||
* @return 旋转后的向量
|
||||
*/
|
||||
inline Vec2 rotateVector(const Vec2 &v, float degrees) {
|
||||
float rad = degrees * DEG_TO_RAD;
|
||||
float cosA = std::cos(rad);
|
||||
float sinA = std::sin(rad);
|
||||
return {v.x * cosA - v.y * sinA, v.x * sinA + v.y * cosA};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 坐标系转换工具
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Y轴向上坐标转Y轴向下坐标
|
||||
* @param pos Y轴向上坐标系中的位置
|
||||
* @param height 画布/屏幕高度
|
||||
* @return Y轴向下坐标系中的位置
|
||||
*/
|
||||
inline Vec2 flipY(const Vec2 &pos, float height) {
|
||||
return {pos.x, height - pos.y};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Y轴向下坐标转Y轴向上坐标
|
||||
* @param pos Y轴向下坐标系中的位置
|
||||
* @param height 画布/屏幕高度
|
||||
* @return Y轴向上坐标系中的位置
|
||||
*/
|
||||
inline Vec2 unflipY(const Vec2 &pos, float height) {
|
||||
return {pos.x, height - pos.y};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 矩阵工具函数
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 从变换矩阵提取位置
|
||||
* @param matrix 4x4变换矩阵
|
||||
* @return 提取的位置向量
|
||||
*/
|
||||
inline Vec2 extractPosition(const glm::mat4 &matrix) {
|
||||
return {matrix[3][0], matrix[3][1]};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从变换矩阵提取缩放
|
||||
* @param matrix 4x4变换矩阵
|
||||
* @return 提取的缩放向量
|
||||
*/
|
||||
inline Vec2 extractScale(const glm::mat4 &matrix) {
|
||||
float scaleX = std::sqrt(matrix[0][0] * matrix[0][0] + matrix[0][1] * matrix[0][1]);
|
||||
float scaleY = std::sqrt(matrix[1][0] * matrix[1][0] + matrix[1][1] * matrix[1][1]);
|
||||
return {scaleX, scaleY};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从变换矩阵提取旋转角度
|
||||
* @param matrix 4x4变换矩阵
|
||||
* @return 提取的旋转角度(度数)
|
||||
*/
|
||||
inline float extractRotation(const glm::mat4 &matrix) {
|
||||
return std::atan2(matrix[0][1], matrix[0][0]) * RAD_TO_DEG;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 碰撞检测工具
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 判断点是否在矩形内
|
||||
* @param point 要检测的点
|
||||
* @param rect 矩形区域
|
||||
* @return 如果点在矩形内返回 true,否则返回 false
|
||||
*/
|
||||
inline bool pointInRect(const Vec2 &point, const Rect &rect) {
|
||||
return point.x >= rect.left() && point.x <= rect.right() &&
|
||||
point.y >= rect.top() && point.y <= rect.bottom();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 判断点是否在圆内
|
||||
* @param point 要检测的点
|
||||
* @param center 圆心
|
||||
* @param radius 圆的半径
|
||||
* @return 如果点在圆内返回 true,否则返回 false
|
||||
*/
|
||||
inline bool pointInCircle(const Vec2 &point, const Vec2 ¢er, float radius) {
|
||||
float dx = point.x - center.x;
|
||||
float dy = point.y - center.y;
|
||||
return (dx * dx + dy * dy) <= (radius * radius);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 判断两个矩形是否相交
|
||||
* @param a 第一个矩形
|
||||
* @param b 第二个矩形
|
||||
* @return 如果矩形相交返回 true,否则返回 false
|
||||
*/
|
||||
inline bool rectsIntersect(const Rect &a, const Rect &b) {
|
||||
return a.intersects(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 判断两个圆是否相交
|
||||
* @param center1 第一个圆的圆心
|
||||
* @param radius1 第一个圆的半径
|
||||
* @param center2 第二个圆的圆心
|
||||
* @param radius2 第二个圆的半径
|
||||
* @return 如果圆相交返回 true,否则返回 false
|
||||
*/
|
||||
inline bool circlesIntersect(const Vec2 ¢er1, float radius1,
|
||||
const Vec2 ¢er2, float radius2) {
|
||||
float dx = center2.x - center1.x;
|
||||
float dy = center2.y - center1.y;
|
||||
float distSq = dx * dx + dy * dy;
|
||||
float radiusSum = radius1 + radius2;
|
||||
return distSq <= (radiusSum * radiusSum);
|
||||
}
|
||||
|
||||
} // namespace math
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <typeindex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Application;
|
||||
|
||||
/**
|
||||
* @brief 模块基类
|
||||
* 所有模块必须继承此类
|
||||
*/
|
||||
class Module {
|
||||
public:
|
||||
virtual ~Module() = default;
|
||||
|
||||
/**
|
||||
* @brief 初始化模块
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
virtual bool init() = 0;
|
||||
|
||||
/**
|
||||
* @brief 关闭模块
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查模块是否已初始化
|
||||
* @return 已初始化返回 true
|
||||
*/
|
||||
virtual bool ok() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取模块名称
|
||||
* @return 模块名称
|
||||
*/
|
||||
virtual const char* name() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取模块优先级(数值越小越优先)
|
||||
* @return 优先级值
|
||||
*/
|
||||
virtual int priority() const { return 100; }
|
||||
|
||||
/**
|
||||
* @brief 获取模块依赖列表
|
||||
* @return 依赖模块类型列表
|
||||
*/
|
||||
virtual std::vector<std::type_index> deps() const { return {}; }
|
||||
|
||||
/**
|
||||
* @brief 设置所属Application
|
||||
* @param app Application指针
|
||||
*/
|
||||
void setApp(class Application* app) { app_ = app; }
|
||||
|
||||
/**
|
||||
* @brief 获取Application
|
||||
* @return Application指针
|
||||
*/
|
||||
class Application* app() const { return app_; }
|
||||
|
||||
protected:
|
||||
class Application* app_ = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 模块工厂函数类型
|
||||
*/
|
||||
using ModuleFactory = std::function<UniquePtr<Module>()>;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <unordered_map>
|
||||
#include <typeindex>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Application;
|
||||
|
||||
/**
|
||||
* @brief 模块注册表
|
||||
* 管理模块的注册、拓扑排序和生命周期
|
||||
*/
|
||||
class Registry {
|
||||
public:
|
||||
static Registry& instance();
|
||||
|
||||
Registry(const Registry&) = delete;
|
||||
Registry& operator=(const Registry&) = delete;
|
||||
|
||||
/**
|
||||
* @brief 注册模块
|
||||
* @tparam T 模块类型
|
||||
* @tparam Args 构造函数参数类型
|
||||
* @param args 构造函数参数
|
||||
* @return 模块指针
|
||||
*/
|
||||
template<typename T, typename... Args>
|
||||
T* use(Args&&... args) {
|
||||
static_assert(std::is_base_of_v<Module, T>, "T must derive from Module");
|
||||
|
||||
auto typeIdx = std::type_index(typeid(T));
|
||||
if (modules_.count(typeIdx)) {
|
||||
return static_cast<T*>(modules_[typeIdx].get());
|
||||
}
|
||||
|
||||
auto module = makeUnique<T>(std::forward<Args>(args)...);
|
||||
T* ptr = module.get();
|
||||
module->setApp(app_);
|
||||
modules_[typeIdx] = std::move(module);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取模块
|
||||
* @tparam T 模块类型
|
||||
* @return 模块指针,不存在返回 nullptr
|
||||
*/
|
||||
template<typename T>
|
||||
T* get() const {
|
||||
auto typeIdx = std::type_index(typeid(T));
|
||||
auto it = modules_.find(typeIdx);
|
||||
if (it != modules_.end()) {
|
||||
return static_cast<T*>(it->second.get());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取模块(基类版本)
|
||||
* @param typeIdx 类型索引
|
||||
* @return 模块指针
|
||||
*/
|
||||
Module* get(std::type_index typeIdx) const {
|
||||
auto it = modules_.find(typeIdx);
|
||||
if (it != modules_.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置Application
|
||||
*/
|
||||
void setApp(Application* app) { app_ = app; }
|
||||
|
||||
/**
|
||||
* @brief 初始化所有模块(按优先级拓扑排序)
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief 关闭所有模块
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 清空所有模块
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 获取模块数量
|
||||
*/
|
||||
size_t size() const { return modules_.size(); }
|
||||
|
||||
private:
|
||||
Registry() = default;
|
||||
~Registry() = default;
|
||||
|
||||
/**
|
||||
* @brief 拓扑排序模块
|
||||
* @return 排序后的模块列表
|
||||
*/
|
||||
std::vector<Module*> topologicalSort();
|
||||
|
||||
std::unordered_map<std::type_index, UniquePtr<Module>> modules_;
|
||||
Application* app_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 服务优先级枚举
|
||||
* 定义服务的初始化顺序,数值越小越先初始化
|
||||
*/
|
||||
enum class ServicePriority : int {
|
||||
Core = 0,
|
||||
Event = 100,
|
||||
Timer = 200,
|
||||
Scene = 300,
|
||||
Camera = 400,
|
||||
Resource = 500,
|
||||
Audio = 600,
|
||||
User = 1000
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务状态枚举
|
||||
*/
|
||||
enum class ServiceState {
|
||||
Uninitialized,
|
||||
Initializing,
|
||||
Running,
|
||||
Paused,
|
||||
Stopping,
|
||||
Stopped
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务信息结构体
|
||||
*/
|
||||
struct ServiceInfo {
|
||||
std::string name;
|
||||
ServicePriority priority = ServicePriority::User;
|
||||
ServiceState state = ServiceState::Uninitialized;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务接口基类
|
||||
* 所有服务必须实现此接口,支持依赖注入和生命周期管理
|
||||
*/
|
||||
class IService {
|
||||
friend class ServiceLocator;
|
||||
|
||||
public:
|
||||
virtual ~IService() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取服务信息
|
||||
* @return 服务信息结构体
|
||||
*/
|
||||
virtual ServiceInfo getServiceInfo() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 初始化服务
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
virtual bool initialize() = 0;
|
||||
|
||||
/**
|
||||
* @brief 关闭服务
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/**
|
||||
* @brief 暂停服务
|
||||
*/
|
||||
virtual void pause() {
|
||||
info_.state = ServiceState::Paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 恢复服务
|
||||
*/
|
||||
virtual void resume() {
|
||||
if (info_.state == ServiceState::Paused) {
|
||||
info_.state = ServiceState::Running;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新服务
|
||||
* @param deltaTime 帧间隔时间
|
||||
*/
|
||||
virtual void update(float deltaTime) { }
|
||||
|
||||
/**
|
||||
* @brief 检查服务是否已初始化
|
||||
* @return 已初始化返回 true
|
||||
*/
|
||||
virtual bool isInitialized() const {
|
||||
return info_.state == ServiceState::Running ||
|
||||
info_.state == ServiceState::Paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取服务状态
|
||||
* @return 当前服务状态
|
||||
*/
|
||||
ServiceState getState() const { return info_.state; }
|
||||
|
||||
/**
|
||||
* @brief 获取服务名称
|
||||
* @return 服务名称
|
||||
*/
|
||||
const std::string& getName() const { return info_.name; }
|
||||
|
||||
protected:
|
||||
ServiceInfo info_;
|
||||
|
||||
/**
|
||||
* @brief 设置服务状态
|
||||
* @param state 新状态
|
||||
*/
|
||||
void setState(ServiceState state) { info_.state = state; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 类型ID生成器
|
||||
* 用于为每种服务类型生成唯一ID
|
||||
*/
|
||||
using ServiceTypeId = size_t;
|
||||
|
||||
namespace detail {
|
||||
inline ServiceTypeId nextServiceTypeId() {
|
||||
static ServiceTypeId id = 0;
|
||||
return ++id;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ServiceTypeId getServiceTypeId() {
|
||||
static ServiceTypeId id = nextServiceTypeId();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <extra2d/core/service_interface.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 服务工厂函数类型
|
||||
*/
|
||||
template <typename T> using ServiceFactory = std::function<SharedPtr<T>()>;
|
||||
|
||||
/**
|
||||
* @brief 服务定位器
|
||||
* 实现依赖注入和服务发现模式,解耦模块间依赖
|
||||
*
|
||||
* 特性:
|
||||
* - 类型安全的服务注册和获取
|
||||
* - 支持服务工厂延迟创建
|
||||
* - 支持服务依赖声明
|
||||
* - 线程安全
|
||||
* - 支持 Mock 测试
|
||||
*/
|
||||
class ServiceLocator {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 服务定位器实例引用
|
||||
*/
|
||||
static ServiceLocator &instance();
|
||||
|
||||
ServiceLocator(const ServiceLocator &) = delete;
|
||||
ServiceLocator &operator=(const ServiceLocator &) = delete;
|
||||
|
||||
/**
|
||||
* @brief 注册服务实例
|
||||
* @tparam T 服务接口类型
|
||||
* @param service 服务实例
|
||||
*/
|
||||
template <typename T> void registerService(SharedPtr<T> service) {
|
||||
static_assert(std::is_base_of_v<IService, T>,
|
||||
"T must derive from IService");
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto typeId = std::type_index(typeid(T));
|
||||
services_[typeId] = std::static_pointer_cast<IService>(service);
|
||||
orderedServices_.push_back(service);
|
||||
sortServices();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册服务工厂
|
||||
* @tparam T 服务接口类型
|
||||
* @param factory 服务工厂函数
|
||||
*/
|
||||
template <typename T> void registerFactory(ServiceFactory<T> factory) {
|
||||
static_assert(std::is_base_of_v<IService, T>,
|
||||
"T must derive from IService");
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto typeId = std::type_index(typeid(T));
|
||||
factories_[typeId] = [factory]() -> SharedPtr<IService> {
|
||||
return std::static_pointer_cast<IService>(factory());
|
||||
};
|
||||
|
||||
// 立即创建服务实例并添加到有序列表
|
||||
auto service = factories_[typeId]();
|
||||
services_[typeId] = service;
|
||||
orderedServices_.push_back(service);
|
||||
sortServices();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取服务实例
|
||||
* @tparam T 服务接口类型
|
||||
* @return 服务实例,不存在返回 nullptr
|
||||
*/
|
||||
template <typename T> SharedPtr<T> getService() const {
|
||||
static_assert(std::is_base_of_v<IService, T>,
|
||||
"T must derive from IService");
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto typeId = std::type_index(typeid(T));
|
||||
|
||||
auto it = services_.find(typeId);
|
||||
if (it != services_.end()) {
|
||||
return std::static_pointer_cast<T>(it->second);
|
||||
}
|
||||
|
||||
auto factoryIt = factories_.find(typeId);
|
||||
if (factoryIt != factories_.end()) {
|
||||
auto service = factoryIt->second();
|
||||
services_[typeId] = service;
|
||||
return std::static_pointer_cast<T>(service);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 尝试获取服务实例(不创建)
|
||||
* @tparam T 服务接口类型
|
||||
* @return 服务实例,不存在返回 nullptr
|
||||
*/
|
||||
template <typename T> SharedPtr<T> tryGetService() const {
|
||||
static_assert(std::is_base_of_v<IService, T>,
|
||||
"T must derive from IService");
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto typeId = std::type_index(typeid(T));
|
||||
auto it = services_.find(typeId);
|
||||
if (it != services_.end()) {
|
||||
return std::static_pointer_cast<T>(it->second);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查服务是否已注册
|
||||
* @tparam T 服务接口类型
|
||||
* @return 已注册返回 true
|
||||
*/
|
||||
template <typename T> bool hasService() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto typeId = std::type_index(typeid(T));
|
||||
return services_.find(typeId) != services_.end() ||
|
||||
factories_.find(typeId) != factories_.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注销服务
|
||||
* @tparam T 服务接口类型
|
||||
*/
|
||||
template <typename T> void unregisterService() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto typeId = std::type_index(typeid(T));
|
||||
|
||||
auto it = services_.find(typeId);
|
||||
if (it != services_.end()) {
|
||||
auto service = it->second;
|
||||
services_.erase(it);
|
||||
|
||||
auto orderIt =
|
||||
std::find(orderedServices_.begin(), orderedServices_.end(), service);
|
||||
if (orderIt != orderedServices_.end()) {
|
||||
orderedServices_.erase(orderIt);
|
||||
}
|
||||
}
|
||||
|
||||
factories_.erase(typeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化所有已注册的服务
|
||||
* @return 所有服务初始化成功返回 true
|
||||
*/
|
||||
bool initializeAll();
|
||||
|
||||
/**
|
||||
* @brief 关闭所有服务
|
||||
*/
|
||||
void shutdownAll();
|
||||
|
||||
/**
|
||||
* @brief 更新所有服务
|
||||
* @param deltaTime 帧间隔时间
|
||||
*/
|
||||
void updateAll(float deltaTime);
|
||||
|
||||
/**
|
||||
* @brief 暂停所有服务
|
||||
*/
|
||||
void pauseAll();
|
||||
|
||||
/**
|
||||
* @brief 恢复所有服务
|
||||
*/
|
||||
void resumeAll();
|
||||
|
||||
/**
|
||||
* @brief 获取所有服务(按优先级排序)
|
||||
* @return 服务列表
|
||||
*/
|
||||
std::vector<SharedPtr<IService>> getAllServices() const;
|
||||
|
||||
/**
|
||||
* @brief 清空所有服务和工厂
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 获取已注册服务数量
|
||||
* @return 服务数量
|
||||
*/
|
||||
size_t size() const { return services_.size(); }
|
||||
|
||||
private:
|
||||
ServiceLocator() = default;
|
||||
~ServiceLocator() = default;
|
||||
|
||||
/**
|
||||
* @brief 按优先级排序服务
|
||||
*/
|
||||
void sortServices();
|
||||
|
||||
mutable std::unordered_map<std::type_index, SharedPtr<IService>> services_;
|
||||
std::unordered_map<std::type_index, std::function<SharedPtr<IService>()>>
|
||||
factories_;
|
||||
std::vector<SharedPtr<IService>> orderedServices_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务注册器
|
||||
* 用于静态注册服务
|
||||
*/
|
||||
template <typename Interface, typename Implementation> class ServiceRegistrar {
|
||||
public:
|
||||
explicit ServiceRegistrar(ServiceFactory<Interface> factory = nullptr) {
|
||||
if (factory) {
|
||||
ServiceLocator::instance().registerFactory<Interface>(factory);
|
||||
} else {
|
||||
ServiceLocator::instance().registerFactory<Interface>(
|
||||
[]() -> SharedPtr<Interface> {
|
||||
return makeShared<Implementation>();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务注册元数据模板
|
||||
* 使用模板元编程实现编译期服务注册
|
||||
* 通过静态成员变量的初始化触发注册
|
||||
*/
|
||||
template <typename Interface, typename Implementation> struct ServiceAutoReg {
|
||||
/**
|
||||
* @brief 注册标记,访问此变量时触发服务注册
|
||||
*/
|
||||
static const bool registered;
|
||||
|
||||
/**
|
||||
* @brief 执行实际的服务注册
|
||||
* @return true 表示注册成功
|
||||
*/
|
||||
static bool doRegister() {
|
||||
::extra2d::ServiceLocator::instance().registerFactory<Interface>(
|
||||
[]() -> ::extra2d::SharedPtr<Interface> {
|
||||
return ::extra2d::makeShared<Implementation>();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// 静态成员定义,在此处触发注册
|
||||
template <typename Interface, typename Implementation>
|
||||
const bool ServiceAutoReg<Interface, Implementation>::registered =
|
||||
ServiceAutoReg<Interface, Implementation>::doRegister();
|
||||
|
||||
/**
|
||||
* @brief 服务注册元数据(带自定义工厂)
|
||||
*/
|
||||
template <typename Interface> struct ServiceAutoRegFactory {
|
||||
template <typename Factory> struct Impl {
|
||||
static const bool registered;
|
||||
|
||||
static bool doRegister(Factory factory) {
|
||||
::extra2d::ServiceLocator::instance().registerFactory<Interface>(factory);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Interface>
|
||||
template <typename Factory>
|
||||
const bool ServiceAutoRegFactory<Interface>::Impl<Factory>::registered =
|
||||
ServiceAutoRegFactory<Interface>::Impl<Factory>::doRegister(Factory{});
|
||||
|
||||
/**
|
||||
* @brief 自动注册服务宏(元数据驱动)
|
||||
* 在服务实现类中使用,通过模板元编程实现自动注册
|
||||
* 比静态对象更可靠,不易被编译器优化
|
||||
*/
|
||||
#define E2D_AUTO_REGISTER_SERVICE(Interface, Implementation) \
|
||||
static inline const bool E2D_CONCAT(_service_reg_, __LINE__) = \
|
||||
ServiceAutoReg<Interface, Implementation>::registered
|
||||
|
||||
/**
|
||||
* @brief 带自定义工厂的自动注册服务宏(元数据驱动)
|
||||
*/
|
||||
#define E2D_AUTO_REGISTER_SERVICE_FACTORY(Interface, Factory) \
|
||||
static inline const bool E2D_CONCAT(_service_factory_reg_, __LINE__) = \
|
||||
ServiceAutoRegFactory<Interface>::Impl<Factory>::registered
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/service_interface.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 服务注册信息
|
||||
*/
|
||||
struct ServiceRegistration {
|
||||
std::string name;
|
||||
ServicePriority priority;
|
||||
std::function<SharedPtr<IService>()> factory;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务注册表
|
||||
* 管理服务的注册信息,支持延迟创建和配置
|
||||
*/
|
||||
class ServiceRegistry {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 服务注册表实例引用
|
||||
*/
|
||||
static ServiceRegistry& instance();
|
||||
|
||||
ServiceRegistry(const ServiceRegistry&) = delete;
|
||||
ServiceRegistry& operator=(const ServiceRegistry&) = delete;
|
||||
|
||||
/**
|
||||
* @brief 注册服务
|
||||
* @tparam T 服务接口类型
|
||||
* @tparam Impl 服务实现类型
|
||||
* @param name 服务名称
|
||||
* @param priority 服务优先级
|
||||
*/
|
||||
template<typename T, typename Impl>
|
||||
void registerService(const std::string& name, ServicePriority priority) {
|
||||
static_assert(std::is_base_of_v<IService, T>,
|
||||
"T must derive from IService");
|
||||
static_assert(std::is_base_of_v<T, Impl>,
|
||||
"Impl must derive from T");
|
||||
|
||||
ServiceRegistration reg;
|
||||
reg.name = name;
|
||||
reg.priority = priority;
|
||||
reg.factory = []() -> SharedPtr<IService> {
|
||||
return std::static_pointer_cast<IService>(makeShared<Impl>());
|
||||
};
|
||||
registrations_.push_back(reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册服务(带工厂函数)
|
||||
* @tparam T 服务接口类型
|
||||
* @param name 服务名称
|
||||
* @param priority 服务优先级
|
||||
* @param factory 工厂函数
|
||||
*/
|
||||
template<typename T>
|
||||
void registerServiceWithFactory(
|
||||
const std::string& name,
|
||||
ServicePriority priority,
|
||||
std::function<SharedPtr<T>()> factory) {
|
||||
static_assert(std::is_base_of_v<IService, T>,
|
||||
"T must derive from IService");
|
||||
|
||||
ServiceRegistration reg;
|
||||
reg.name = name;
|
||||
reg.priority = priority;
|
||||
reg.factory = [factory]() -> SharedPtr<IService> {
|
||||
return std::static_pointer_cast<IService>(factory());
|
||||
};
|
||||
registrations_.push_back(reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启用/禁用服务
|
||||
* @param name 服务名称
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void setServiceEnabled(const std::string& name, bool enabled);
|
||||
|
||||
/**
|
||||
* @brief 创建所有已注册的服务
|
||||
* 并注册到 ServiceLocator
|
||||
*/
|
||||
void createAllServices();
|
||||
|
||||
/**
|
||||
* @brief 获取所有注册信息
|
||||
* @return 注册信息列表
|
||||
*/
|
||||
const std::vector<ServiceRegistration>& getRegistrations() const {
|
||||
return registrations_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空所有注册
|
||||
*/
|
||||
void clear() {
|
||||
registrations_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
ServiceRegistry() = default;
|
||||
~ServiceRegistry() = default;
|
||||
|
||||
std::vector<ServiceRegistration> registrations_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 自动服务注册器
|
||||
* 在全局作用域使用,自动注册服务
|
||||
*/
|
||||
template<typename Interface, typename Implementation>
|
||||
class AutoServiceRegistrar {
|
||||
public:
|
||||
AutoServiceRegistrar(const std::string& name, ServicePriority priority) {
|
||||
ServiceRegistry::instance().registerService<Interface, Implementation>(
|
||||
name, priority);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#define E2D_REGISTER_SERVICE_AUTO(Interface, Implementation, Name, Priority) \
|
||||
namespace { \
|
||||
static ::extra2d::AutoServiceRegistrar<Interface, Implementation> \
|
||||
E2D_CONCAT(auto_service_registrar_, __LINE__)(Name, Priority); \
|
||||
}
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 字符串编码转换工具函数
|
||||
// 统一使用 std::string (UTF-8) 作为项目标准字符串类型
|
||||
// ============================================================================
|
||||
|
||||
// UTF-8 ↔ UTF-16 转换
|
||||
std::u16string utf8ToUtf16(const std::string& utf8);
|
||||
std::string utf16ToUtf8(const std::u16string& utf16);
|
||||
|
||||
// UTF-8 ↔ UTF-32 转换
|
||||
std::u32string utf8ToUtf32(const std::string& utf8);
|
||||
std::string utf32ToUtf8(const std::u32string& utf32);
|
||||
|
||||
// UTF-8 ↔ Wide String 转换
|
||||
std::wstring utf8ToWide(const std::string& utf8);
|
||||
std::string wideToUtf8(const std::wstring& wide);
|
||||
|
||||
// UTF-8 ↔ GBK/GB2312 转换(Windows 中文系统常用)
|
||||
std::string utf8ToGbk(const std::string& utf8);
|
||||
std::string gbkToUtf8(const std::string& gbk);
|
||||
|
||||
// ============================================================================
|
||||
// 内联实现
|
||||
// ============================================================================
|
||||
|
||||
inline std::u16string utf8ToUtf16(const std::string& utf8) {
|
||||
if (utf8.empty()) return std::u16string();
|
||||
|
||||
// UTF-8 → UTF-32 → UTF-16 (with surrogate pairs)
|
||||
std::u32string u32 = utf8ToUtf32(utf8);
|
||||
std::u16string result;
|
||||
result.reserve(u32.size());
|
||||
|
||||
for (char32_t ch : u32) {
|
||||
if (ch <= 0xFFFF) {
|
||||
result.push_back(static_cast<char16_t>(ch));
|
||||
} else if (ch <= 0x10FFFF) {
|
||||
// Surrogate pair
|
||||
ch -= 0x10000;
|
||||
result.push_back(static_cast<char16_t>(0xD800 | (ch >> 10)));
|
||||
result.push_back(static_cast<char16_t>(0xDC00 | (ch & 0x3FF)));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::string utf16ToUtf8(const std::u16string& utf16) {
|
||||
if (utf16.empty()) return std::string();
|
||||
|
||||
// UTF-16 → UTF-32 → UTF-8
|
||||
std::u32string u32;
|
||||
u32.reserve(utf16.size());
|
||||
|
||||
for (size_t i = 0; i < utf16.size(); ++i) {
|
||||
char16_t cu = utf16[i];
|
||||
char32_t ch;
|
||||
if (cu >= 0xD800 && cu <= 0xDBFF && i + 1 < utf16.size()) {
|
||||
// High surrogate
|
||||
char16_t cl = utf16[i + 1];
|
||||
if (cl >= 0xDC00 && cl <= 0xDFFF) {
|
||||
ch = 0x10000 + ((static_cast<char32_t>(cu - 0xD800) << 10) |
|
||||
(cl - 0xDC00));
|
||||
++i;
|
||||
} else {
|
||||
ch = cu; // Invalid, pass through
|
||||
}
|
||||
} else {
|
||||
ch = cu;
|
||||
}
|
||||
u32.push_back(ch);
|
||||
}
|
||||
|
||||
return utf32ToUtf8(u32);
|
||||
}
|
||||
|
||||
inline std::u32string utf8ToUtf32(const std::string& utf8) {
|
||||
std::u32string result;
|
||||
result.reserve(utf8.size());
|
||||
|
||||
const char* ptr = utf8.c_str();
|
||||
const char* end = ptr + utf8.size();
|
||||
|
||||
while (ptr < end) {
|
||||
char32_t ch = 0;
|
||||
unsigned char byte = static_cast<unsigned char>(*ptr);
|
||||
|
||||
if ((byte & 0x80) == 0) {
|
||||
// 1-byte sequence
|
||||
ch = byte;
|
||||
ptr += 1;
|
||||
} else if ((byte & 0xE0) == 0xC0) {
|
||||
// 2-byte sequence
|
||||
ch = (byte & 0x1F) << 6;
|
||||
ch |= (static_cast<unsigned char>(ptr[1]) & 0x3F);
|
||||
ptr += 2;
|
||||
} else if ((byte & 0xF0) == 0xE0) {
|
||||
// 3-byte sequence
|
||||
ch = (byte & 0x0F) << 12;
|
||||
ch |= (static_cast<unsigned char>(ptr[1]) & 0x3F) << 6;
|
||||
ch |= (static_cast<unsigned char>(ptr[2]) & 0x3F);
|
||||
ptr += 3;
|
||||
} else if ((byte & 0xF8) == 0xF0) {
|
||||
// 4-byte sequence
|
||||
ch = (byte & 0x07) << 18;
|
||||
ch |= (static_cast<unsigned char>(ptr[1]) & 0x3F) << 12;
|
||||
ch |= (static_cast<unsigned char>(ptr[2]) & 0x3F) << 6;
|
||||
ch |= (static_cast<unsigned char>(ptr[3]) & 0x3F);
|
||||
ptr += 4;
|
||||
} else {
|
||||
// Invalid UTF-8, skip
|
||||
ptr += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push_back(ch);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::string utf32ToUtf8(const std::u32string& utf32) {
|
||||
std::string result;
|
||||
|
||||
for (char32_t ch : utf32) {
|
||||
if (ch <= 0x7F) {
|
||||
// 1-byte
|
||||
result.push_back(static_cast<char>(ch));
|
||||
} else if (ch <= 0x7FF) {
|
||||
// 2-byte
|
||||
result.push_back(static_cast<char>(0xC0 | ((ch >> 6) & 0x1F)));
|
||||
result.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
||||
} else if (ch <= 0xFFFF) {
|
||||
// 3-byte
|
||||
result.push_back(static_cast<char>(0xE0 | ((ch >> 12) & 0x0F)));
|
||||
result.push_back(static_cast<char>(0x80 | ((ch >> 6) & 0x3F)));
|
||||
result.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
||||
} else if (ch <= 0x10FFFF) {
|
||||
// 4-byte
|
||||
result.push_back(static_cast<char>(0xF0 | ((ch >> 18) & 0x07)));
|
||||
result.push_back(static_cast<char>(0x80 | ((ch >> 12) & 0x3F)));
|
||||
result.push_back(static_cast<char>(0x80 | ((ch >> 6) & 0x3F)));
|
||||
result.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::wstring utf8ToWide(const std::string& utf8) {
|
||||
if (utf8.empty()) return std::wstring();
|
||||
|
||||
if constexpr (sizeof(wchar_t) == 4) {
|
||||
// wchar_t is 32-bit (Linux/Switch): same as UTF-32
|
||||
std::u32string u32 = utf8ToUtf32(utf8);
|
||||
return std::wstring(u32.begin(), u32.end());
|
||||
} else {
|
||||
// wchar_t is 16-bit (Windows): same as UTF-16
|
||||
std::u16string u16 = utf8ToUtf16(utf8);
|
||||
return std::wstring(u16.begin(), u16.end());
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string wideToUtf8(const std::wstring& wide) {
|
||||
if (wide.empty()) return std::string();
|
||||
|
||||
if constexpr (sizeof(wchar_t) == 4) {
|
||||
std::u32string u32(wide.begin(), wide.end());
|
||||
return utf32ToUtf8(u32);
|
||||
} else {
|
||||
std::u16string u16(wide.begin(), wide.end());
|
||||
return utf16ToUtf8(u16);
|
||||
}
|
||||
}
|
||||
|
||||
// GBK/GB2312 转换(Windows 平台实现)
|
||||
// 注意:Windows 实现在 .cpp 文件中,避免头文件包含 windows.h 导致冲突
|
||||
#ifdef _WIN32
|
||||
// 前向声明,实现在 .cpp 文件中
|
||||
std::string utf8ToGbkImpl(const std::string& utf8);
|
||||
std::string gbkToUtf8Impl(const std::string& gbk);
|
||||
|
||||
inline std::string utf8ToGbk(const std::string& utf8) {
|
||||
return utf8ToGbkImpl(utf8);
|
||||
}
|
||||
|
||||
inline std::string gbkToUtf8(const std::string& gbk) {
|
||||
return gbkToUtf8Impl(gbk);
|
||||
}
|
||||
#else
|
||||
// 非 Windows 平台,GBK 转换使用 iconv 或返回原字符串
|
||||
inline std::string utf8ToGbk(const std::string& utf8) {
|
||||
// TODO: 使用 iconv 实现
|
||||
return utf8;
|
||||
}
|
||||
|
||||
inline std::string gbkToUtf8(const std::string& gbk) {
|
||||
// TODO: 使用 iconv 实现
|
||||
return gbk;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -7,10 +7,17 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 宏定义
|
||||
// ---------------------------------------------------------------------------
|
||||
#define E2D_CONCAT_IMPL(a, b) a##b
|
||||
#define E2D_CONCAT(a, b) E2D_CONCAT_IMPL(a, b)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 智能指针别名
|
||||
// ---------------------------------------------------------------------------
|
||||
template <typename T> using Ptr = std::shared_ptr<T>;
|
||||
template <typename T> using SharedPtr = std::shared_ptr<T>;
|
||||
|
||||
template <typename T> using UniquePtr = std::unique_ptr<T>;
|
||||
|
||||
|
|
@ -21,6 +28,10 @@ template <typename T, typename... Args> inline Ptr<T> makePtr(Args &&...args) {
|
|||
return std::make_shared<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args> inline SharedPtr<T> makeShared(Args &&...args) {
|
||||
return std::make_shared<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// 创建 unique_ptr 的便捷函数
|
||||
template <typename T, typename... Args>
|
||||
inline UniquePtr<T> makeUnique(Args &&...args) {
|
||||
|
|
|
|||
|
|
@ -1,324 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/effects/particle_system.h>
|
||||
#include <extra2d/effects/post_process.h>
|
||||
#include <extra2d/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 <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/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 <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/opengl/gl_shader.h>
|
||||
#include <extra2d/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
|
||||
|
|
@ -1,212 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
// SDL2 键码定义
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 键盘按键码 (基于 SDL2)
|
||||
// ============================================================================
|
||||
namespace Key {
|
||||
enum : int {
|
||||
Unknown = SDLK_UNKNOWN,
|
||||
Space = SDLK_SPACE,
|
||||
Apostrophe = SDLK_QUOTE,
|
||||
Comma = SDLK_COMMA,
|
||||
Minus = SDLK_MINUS,
|
||||
Period = SDLK_PERIOD,
|
||||
Slash = SDLK_SLASH,
|
||||
Num0 = SDLK_0,
|
||||
Num1 = SDLK_1,
|
||||
Num2 = SDLK_2,
|
||||
Num3 = SDLK_3,
|
||||
Num4 = SDLK_4,
|
||||
Num5 = SDLK_5,
|
||||
Num6 = SDLK_6,
|
||||
Num7 = SDLK_7,
|
||||
Num8 = SDLK_8,
|
||||
Num9 = SDLK_9,
|
||||
Semicolon = SDLK_SEMICOLON,
|
||||
Equal = SDLK_EQUALS,
|
||||
A = SDLK_a,
|
||||
B = SDLK_b,
|
||||
C = SDLK_c,
|
||||
D = SDLK_d,
|
||||
E = SDLK_e,
|
||||
F = SDLK_f,
|
||||
G = SDLK_g,
|
||||
H = SDLK_h,
|
||||
I = SDLK_i,
|
||||
J = SDLK_j,
|
||||
K = SDLK_k,
|
||||
L = SDLK_l,
|
||||
M = SDLK_m,
|
||||
N = SDLK_n,
|
||||
O = SDLK_o,
|
||||
P = SDLK_p,
|
||||
Q = SDLK_q,
|
||||
R = SDLK_r,
|
||||
S = SDLK_s,
|
||||
T = SDLK_t,
|
||||
U = SDLK_u,
|
||||
V = SDLK_v,
|
||||
W = SDLK_w,
|
||||
X = SDLK_x,
|
||||
Y = SDLK_y,
|
||||
Z = SDLK_z,
|
||||
LeftBracket = SDLK_LEFTBRACKET,
|
||||
Backslash = SDLK_BACKSLASH,
|
||||
RightBracket = SDLK_RIGHTBRACKET,
|
||||
GraveAccent = SDLK_BACKQUOTE,
|
||||
Escape = SDLK_ESCAPE,
|
||||
Enter = SDLK_RETURN,
|
||||
Tab = SDLK_TAB,
|
||||
Backspace = SDLK_BACKSPACE,
|
||||
Insert = SDLK_INSERT,
|
||||
Delete = SDLK_DELETE,
|
||||
Right = SDLK_RIGHT,
|
||||
Left = SDLK_LEFT,
|
||||
Down = SDLK_DOWN,
|
||||
Up = SDLK_UP,
|
||||
PageUp = SDLK_PAGEUP,
|
||||
PageDown = SDLK_PAGEDOWN,
|
||||
Home = SDLK_HOME,
|
||||
End = SDLK_END,
|
||||
CapsLock = SDLK_CAPSLOCK,
|
||||
ScrollLock = SDLK_SCROLLLOCK,
|
||||
NumLock = SDLK_NUMLOCKCLEAR,
|
||||
PrintScreen = SDLK_PRINTSCREEN,
|
||||
Pause = SDLK_PAUSE,
|
||||
F1 = SDLK_F1,
|
||||
F2 = SDLK_F2,
|
||||
F3 = SDLK_F3,
|
||||
F4 = SDLK_F4,
|
||||
F5 = SDLK_F5,
|
||||
F6 = SDLK_F6,
|
||||
F7 = SDLK_F7,
|
||||
F8 = SDLK_F8,
|
||||
F9 = SDLK_F9,
|
||||
F10 = SDLK_F10,
|
||||
F11 = SDLK_F11,
|
||||
F12 = SDLK_F12,
|
||||
F13 = SDLK_F13,
|
||||
F14 = SDLK_F14,
|
||||
F15 = SDLK_F15,
|
||||
F16 = SDLK_F16,
|
||||
F17 = SDLK_F17,
|
||||
F18 = SDLK_F18,
|
||||
F19 = SDLK_F19,
|
||||
F20 = SDLK_F20,
|
||||
F21 = SDLK_F21,
|
||||
F22 = SDLK_F22,
|
||||
F23 = SDLK_F23,
|
||||
F24 = SDLK_F24,
|
||||
KP0 = SDLK_KP_0,
|
||||
KP1 = SDLK_KP_1,
|
||||
KP2 = SDLK_KP_2,
|
||||
KP3 = SDLK_KP_3,
|
||||
KP4 = SDLK_KP_4,
|
||||
KP5 = SDLK_KP_5,
|
||||
KP6 = SDLK_KP_6,
|
||||
KP7 = SDLK_KP_7,
|
||||
KP8 = SDLK_KP_8,
|
||||
KP9 = SDLK_KP_9,
|
||||
KPDecimal = SDLK_KP_PERIOD,
|
||||
KPDivide = SDLK_KP_DIVIDE,
|
||||
KPMultiply = SDLK_KP_MULTIPLY,
|
||||
KPSubtract = SDLK_KP_MINUS,
|
||||
KPAdd = SDLK_KP_PLUS,
|
||||
KPEnter = SDLK_KP_ENTER,
|
||||
KPEqual = SDLK_KP_EQUALS,
|
||||
LeftShift = SDLK_LSHIFT,
|
||||
LeftControl = SDLK_LCTRL,
|
||||
LeftAlt = SDLK_LALT,
|
||||
LeftSuper = SDLK_LGUI,
|
||||
RightShift = SDLK_RSHIFT,
|
||||
RightControl = SDLK_RCTRL,
|
||||
RightAlt = SDLK_RALT,
|
||||
RightSuper = SDLK_RGUI,
|
||||
Menu = SDLK_MENU,
|
||||
Last = SDLK_MENU
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 修饰键
|
||||
// ============================================================================
|
||||
namespace Mod {
|
||||
enum : int {
|
||||
Shift = KMOD_SHIFT,
|
||||
Control = KMOD_CTRL,
|
||||
Alt = KMOD_ALT,
|
||||
Super = KMOD_GUI,
|
||||
CapsLock = KMOD_CAPS,
|
||||
NumLock = KMOD_NUM
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 鼠标按键码
|
||||
// ============================================================================
|
||||
namespace Mouse {
|
||||
enum : int {
|
||||
Button1 = 0,
|
||||
Button2 = 1,
|
||||
Button3 = 2,
|
||||
Button4 = 3,
|
||||
Button5 = 4,
|
||||
Button6 = 5,
|
||||
Button7 = 6,
|
||||
Button8 = 7,
|
||||
ButtonLast = Button8,
|
||||
ButtonLeft = Button1,
|
||||
ButtonRight = Button2,
|
||||
ButtonMiddle = Button3
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 游戏手柄按键
|
||||
// ============================================================================
|
||||
namespace GamepadButton {
|
||||
enum : int {
|
||||
A = SDL_CONTROLLER_BUTTON_A,
|
||||
B = SDL_CONTROLLER_BUTTON_B,
|
||||
X = SDL_CONTROLLER_BUTTON_X,
|
||||
Y = SDL_CONTROLLER_BUTTON_Y,
|
||||
LeftBumper = SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
|
||||
RightBumper = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
|
||||
Back = SDL_CONTROLLER_BUTTON_BACK,
|
||||
Start = SDL_CONTROLLER_BUTTON_START,
|
||||
Guide = SDL_CONTROLLER_BUTTON_GUIDE,
|
||||
LeftThumb = SDL_CONTROLLER_BUTTON_LEFTSTICK,
|
||||
RightThumb = SDL_CONTROLLER_BUTTON_RIGHTSTICK,
|
||||
DPadUp = SDL_CONTROLLER_BUTTON_DPAD_UP,
|
||||
DPadRight = SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
|
||||
DPadDown = SDL_CONTROLLER_BUTTON_DPAD_DOWN,
|
||||
DPadLeft = SDL_CONTROLLER_BUTTON_DPAD_LEFT,
|
||||
Last = SDL_CONTROLLER_BUTTON_DPAD_LEFT,
|
||||
Cross = A,
|
||||
Circle = B,
|
||||
Square = X,
|
||||
Triangle = Y
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 游戏手柄轴
|
||||
// ============================================================================
|
||||
namespace GamepadAxis {
|
||||
enum : int {
|
||||
LeftX = SDL_CONTROLLER_AXIS_LEFTX,
|
||||
LeftY = SDL_CONTROLLER_AXIS_LEFTY,
|
||||
RightX = SDL_CONTROLLER_AXIS_RIGHTX,
|
||||
RightY = SDL_CONTROLLER_AXIS_RIGHTY,
|
||||
LeftTrigger = SDL_CONTROLLER_AXIS_TRIGGERLEFT,
|
||||
RightTrigger = SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
|
||||
Last = SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,110 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
// Easy2D v3.0 - 统一入口头文件
|
||||
// Extra2D - 统一入口头文件
|
||||
// 包含所有公共 API
|
||||
|
||||
// Core
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/string.h>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/core/registry.h>
|
||||
|
||||
// Config removed - app info now in Application class
|
||||
|
||||
// Platform
|
||||
#include <extra2d/platform/window.h>
|
||||
#include <extra2d/platform/input.h>
|
||||
#include <extra2d/platform/iinput.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <extra2d/platform/keys.h>
|
||||
#include <extra2d/platform/input_module.h>
|
||||
#include <extra2d/platform/backend_factory.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
|
||||
// Graphics
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/graphics/camera.h>
|
||||
#include <extra2d/graphics/shader_system.h>
|
||||
|
||||
#include <extra2d/graphics/render_target.h>
|
||||
#include <extra2d/graphics/vram_manager.h>
|
||||
#include <extra2d/graphics/camera/camera.h>
|
||||
#include <extra2d/graphics/texture/font.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/graphics/core/render_module.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/graphics/core/render_target.h>
|
||||
#include <extra2d/graphics/camera/viewport_adapter.h>
|
||||
#include <extra2d/graphics/memory/vram_manager.h>
|
||||
#include <extra2d/graphics/texture/texture_pool.h>
|
||||
|
||||
// Scene
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <extra2d/scene/scene.h>
|
||||
#include <extra2d/scene/sprite.h>
|
||||
#include <extra2d/scene/shape_node.h>
|
||||
#include <extra2d/scene/scene_manager.h>
|
||||
#include <extra2d/scene/transition_scene.h>
|
||||
#include <extra2d/scene/transition_fade_scene.h>
|
||||
#include <extra2d/scene/transition_slide_scene.h>
|
||||
#include <extra2d/scene/transition_scale_scene.h>
|
||||
#include <extra2d/scene/transition_flip_scene.h>
|
||||
#include <extra2d/scene/transition_box_scene.h>
|
||||
|
||||
// Animation
|
||||
#include <extra2d/animation/sprite_frame.h>
|
||||
#include <extra2d/animation/sprite_frame_cache.h>
|
||||
#include <extra2d/animation/frame_property.h>
|
||||
#include <extra2d/animation/animation_frame.h>
|
||||
#include <extra2d/animation/animation_clip.h>
|
||||
#include <extra2d/animation/animation_controller.h>
|
||||
#include <extra2d/animation/animation_cache.h>
|
||||
#include <extra2d/animation/interpolation_engine.h>
|
||||
#include <extra2d/animation/animated_sprite.h>
|
||||
#include <extra2d/animation/frame_renderer.h>
|
||||
#include <extra2d/animation/animation_event.h>
|
||||
#include <extra2d/animation/animation_node.h>
|
||||
#include <extra2d/animation/composite_animation.h>
|
||||
#include <extra2d/animation/ani_parser.h>
|
||||
#include <extra2d/animation/ani_binary_parser.h>
|
||||
#include <extra2d/animation/als_parser.h>
|
||||
|
||||
// UI
|
||||
#include <extra2d/ui/widget.h>
|
||||
#include <extra2d/ui/button.h>
|
||||
#include <extra2d/ui/text.h>
|
||||
#include <extra2d/ui/label.h>
|
||||
#include <extra2d/ui/progress_bar.h>
|
||||
#include <extra2d/ui/check_box.h>
|
||||
#include <extra2d/ui/radio_button.h>
|
||||
#include <extra2d/ui/slider.h>
|
||||
|
||||
// Action
|
||||
#include <extra2d/action/action.h>
|
||||
#include <extra2d/action/finite_time_action.h>
|
||||
#include <extra2d/action/action_interval.h>
|
||||
#include <extra2d/action/action_instant.h>
|
||||
#include <extra2d/action/action_interval_actions.h>
|
||||
#include <extra2d/action/action_instant_actions.h>
|
||||
#include <extra2d/action/action_ease.h>
|
||||
#include <extra2d/action/action_special.h>
|
||||
#include <extra2d/action/action_manager.h>
|
||||
#include <extra2d/action/ease.h>
|
||||
#include <extra2d/scene/shape_node.h>
|
||||
#include <extra2d/scene/sprite.h>
|
||||
|
||||
// Event
|
||||
#include <extra2d/event/event.h>
|
||||
#include <extra2d/event/event_queue.h>
|
||||
#include <extra2d/event/event_dispatcher.h>
|
||||
#include <extra2d/event/input_codes.h>
|
||||
|
||||
// Audio
|
||||
#include <extra2d/audio/audio_engine.h>
|
||||
#include <extra2d/audio/sound.h>
|
||||
|
||||
// Resource
|
||||
#include <extra2d/resource/resource_manager.h>
|
||||
#include <extra2d/event/event_queue.h>
|
||||
|
||||
// Utils
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/utils/timer.h>
|
||||
#include <extra2d/utils/data.h>
|
||||
#include <extra2d/utils/random.h>
|
||||
#include <extra2d/utils/timer.h>
|
||||
|
||||
// Spatial
|
||||
#include <extra2d/spatial/spatial_index.h>
|
||||
#include <extra2d/spatial/quadtree.h>
|
||||
#include <extra2d/spatial/spatial_hash.h>
|
||||
#include <extra2d/spatial/spatial_manager.h>
|
||||
|
||||
// Effects
|
||||
#include <extra2d/effects/post_process.h>
|
||||
#include <extra2d/effects/particle_system.h>
|
||||
#include <extra2d/effects/custom_effect_manager.h>
|
||||
// Services
|
||||
#include <extra2d/services/event_service.h>
|
||||
#include <extra2d/services/scene_service.h>
|
||||
#include <extra2d/services/timer_service.h>
|
||||
#include <extra2d/services/camera_service.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
// Application
|
||||
#include <extra2d/app/application.h>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/core/smart_ptr.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 渲染后端类型枚举
|
||||
*/
|
||||
enum class BackendType {
|
||||
OpenGL, // OpenGL 4.x
|
||||
Vulkan, // Vulkan 1.x
|
||||
Metal, // Metal (macOS/iOS)
|
||||
D3D11, // Direct3D 11
|
||||
D3D12, // Direct3D 12
|
||||
OpenGLES, // OpenGL ES (移动平台)
|
||||
Count
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 后端工厂类,用于创建渲染后端实例
|
||||
*/
|
||||
class BackendFactory {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取后端工厂单例
|
||||
* @return 后端工厂实例引用
|
||||
*/
|
||||
static BackendFactory& getInstance();
|
||||
|
||||
/**
|
||||
* @brief 创建渲染后端
|
||||
* @param type 后端类型
|
||||
* @return 渲染后端实例
|
||||
*/
|
||||
UniquePtr<RenderBackend> createBackend(BackendType type);
|
||||
|
||||
/**
|
||||
* @brief 创建默认渲染后端
|
||||
* @return 默认渲染后端实例
|
||||
*/
|
||||
UniquePtr<RenderBackend> createDefaultBackend();
|
||||
|
||||
/**
|
||||
* @brief 检查后端是否可用
|
||||
* @param type 后端类型
|
||||
* @return 可用返回true,否则返回false
|
||||
*/
|
||||
bool isBackendAvailable(BackendType type) const;
|
||||
|
||||
/**
|
||||
* @brief 获取当前平台推荐的后端类型
|
||||
* @return 推荐的后端类型
|
||||
*/
|
||||
BackendType getRecommendedBackend() const;
|
||||
|
||||
/**
|
||||
* @brief 获取后端类型名称
|
||||
* @param type 后端类型
|
||||
* @return 后端类型名称字符串
|
||||
*/
|
||||
const char* getBackendName(BackendType type) const;
|
||||
|
||||
/**
|
||||
* @brief 从名称解析后端类型
|
||||
* @param name 后端类型名称
|
||||
* @return 后端类型,如果未知则返回OpenGL
|
||||
*/
|
||||
BackendType parseBackendType(const char* name) const;
|
||||
|
||||
private:
|
||||
BackendFactory() = default;
|
||||
~BackendFactory() = default;
|
||||
BackendFactory(const BackendFactory&) = delete;
|
||||
BackendFactory& operator=(const BackendFactory&) = delete;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/resources/buffer.h>
|
||||
#include <glad/glad.h>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 缓冲区实现
|
||||
// ============================================================================
|
||||
class GLBuffer : public Buffer {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLBuffer();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLBuffer() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化缓冲区
|
||||
* @param desc 缓冲区描述
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(const BufferDesc& desc);
|
||||
|
||||
/**
|
||||
* @brief 关闭缓冲区,释放资源
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
// Buffer 接口实现
|
||||
void bind() override;
|
||||
void unbind() override;
|
||||
void setData(const void* data, size_t size) override;
|
||||
void updateData(const void* data, size_t offset, size_t size) override;
|
||||
void* map() override;
|
||||
void unmap() override;
|
||||
size_t getSize() const override { return size_; }
|
||||
BufferType getType() const override { return type_; }
|
||||
BufferUsage getUsage() const override { return usage_; }
|
||||
bool isValid() const override { return bufferID_ != 0; }
|
||||
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(bufferID_); }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 缓冲区 ID
|
||||
* @return 缓冲区 ID
|
||||
*/
|
||||
GLuint getBufferID() const { return bufferID_; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 缓冲区目标类型
|
||||
* @return 缓冲区目标类型
|
||||
*/
|
||||
GLenum getTarget() const { return target_; }
|
||||
|
||||
private:
|
||||
GLuint bufferID_ = 0;
|
||||
GLenum target_ = GL_ARRAY_BUFFER;
|
||||
size_t size_ = 0;
|
||||
BufferType type_ = BufferType::Vertex;
|
||||
BufferUsage usage_ = BufferUsage::Static;
|
||||
GLenum glUsage_ = GL_STATIC_DRAW;
|
||||
bool mapped_ = false;
|
||||
void* mappedPtr_ = nullptr;
|
||||
|
||||
/**
|
||||
* @brief 转换使用模式到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertUsage(BufferUsage usage);
|
||||
|
||||
/**
|
||||
* @brief 转换缓冲区类型到 OpenGL 目标
|
||||
*/
|
||||
static GLenum convertType(BufferType type);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
#pragma once
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 版本信息
|
||||
// ============================================================================
|
||||
struct GLVersion {
|
||||
int major = 0;
|
||||
int minor = 0;
|
||||
bool es = false; // 是否为 ES 版本
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 上下文管理类
|
||||
// ============================================================================
|
||||
class GLContext {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取全局 GLContext 实例
|
||||
*/
|
||||
static GLContext& get();
|
||||
|
||||
/**
|
||||
* @brief 初始化 OpenGL 上下文
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief 关闭 OpenGL 上下文
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查上下文是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
bool isValid() const { return initialized_; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 版本信息
|
||||
*/
|
||||
const GLVersion& getVersion() const { return version_; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 版本字符串
|
||||
*/
|
||||
std::string getVersionString() const;
|
||||
|
||||
/**
|
||||
* @brief 获取 GPU 厂商信息
|
||||
*/
|
||||
std::string getVendor() const;
|
||||
|
||||
/**
|
||||
* @brief 获取 GPU 渲染器信息
|
||||
*/
|
||||
std::string getRenderer() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否支持指定扩展
|
||||
* @param extension 扩展名称
|
||||
* @return 支持返回 true
|
||||
*/
|
||||
bool hasExtension(const std::string& extension) const;
|
||||
|
||||
/**
|
||||
* @brief 获取最大纹理尺寸
|
||||
*/
|
||||
int getMaxTextureSize() const;
|
||||
|
||||
/**
|
||||
* @brief 获取最大纹理单元数
|
||||
*/
|
||||
int getMaxTextureUnits() const;
|
||||
|
||||
/**
|
||||
* @brief 获取最大顶点属性数
|
||||
*/
|
||||
int getMaxVertexAttribs() const;
|
||||
|
||||
/**
|
||||
* @brief 获取最大 uniform 缓冲区绑定点数
|
||||
*/
|
||||
int getMaxUniformBufferBindings() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否为 OpenGL ES
|
||||
*/
|
||||
bool isGLES() const { return version_.es; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否支持 VAO
|
||||
*/
|
||||
bool hasVAO() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否支持 FBO
|
||||
*/
|
||||
bool hasFBO() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否支持 Shader
|
||||
*/
|
||||
bool hasShader() const;
|
||||
|
||||
private:
|
||||
GLContext() = default;
|
||||
~GLContext() = default;
|
||||
|
||||
GLContext(const GLContext&) = delete;
|
||||
GLContext& operator=(const GLContext&) = delete;
|
||||
|
||||
bool initialized_ = false;
|
||||
GLVersion version_;
|
||||
|
||||
// 缓存的限制值
|
||||
mutable int maxTextureSize_ = -1;
|
||||
mutable int maxTextureUnits_ = -1;
|
||||
mutable int maxVertexAttribs_ = -1;
|
||||
mutable int maxUniformBufferBindings_ = -1;
|
||||
|
||||
/**
|
||||
* @brief 解析 OpenGL 版本
|
||||
*/
|
||||
void parseVersion();
|
||||
|
||||
/**
|
||||
* @brief 加载 OpenGL 扩展
|
||||
*/
|
||||
bool loadExtensions();
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/texture/font.h>
|
||||
|
||||
#include <stb/stb_truetype.h>
|
||||
#include <stb/stb_rect_pack.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 字体图集实现 (使用 STB 库)
|
||||
// 使用 stb_rect_pack 进行动态矩形打包,支持动态缓存字形
|
||||
// ============================================================================
|
||||
class GLFontAtlas : public FontAtlas {
|
||||
public:
|
||||
GLFontAtlas(const std::string& filepath, int fontSize, bool useSDF = false);
|
||||
~GLFontAtlas() override;
|
||||
|
||||
// FontAtlas 接口实现
|
||||
const Glyph* getGlyph(char32_t codepoint) const override;
|
||||
Texture* getTexture() const override { return texture_.get(); }
|
||||
int getFontSize() const override { return fontSize_; }
|
||||
float getAscent() const override { return ascent_; }
|
||||
float getDescent() const override { return descent_; }
|
||||
float getLineGap() const override { return lineGap_; }
|
||||
float getLineHeight() const override { return lineHeight_; }
|
||||
bool isSDF() const override { return useSDF_; }
|
||||
Vec2 measureText(const std::string& text) override;
|
||||
|
||||
private:
|
||||
// 字形数据内部结构
|
||||
struct GlyphData {
|
||||
float width;
|
||||
float height;
|
||||
float bearingX;
|
||||
float bearingY;
|
||||
float advance;
|
||||
float u0, v0, u1, v1;
|
||||
};
|
||||
|
||||
// 图集配置 - 增大尺寸以支持更多字符
|
||||
static constexpr int ATLAS_WIDTH = 1024;
|
||||
static constexpr int ATLAS_HEIGHT = 1024;
|
||||
static constexpr int PADDING = 2; // 字形之间的间距
|
||||
|
||||
bool useSDF_;
|
||||
int fontSize_;
|
||||
|
||||
Ptr<GLTexture> texture_;
|
||||
std::unordered_map<char32_t, GlyphData> glyphs_;
|
||||
float lineHeight_;
|
||||
float ascent_;
|
||||
float descent_;
|
||||
float lineGap_;
|
||||
|
||||
// 字体数据
|
||||
std::vector<unsigned char> fontData_;
|
||||
stbtt_fontinfo fontInfo_;
|
||||
float scale_;
|
||||
|
||||
// stb_rect_pack 上下文 - 持久化以支持增量打包
|
||||
mutable stbrp_context packContext_;
|
||||
mutable std::vector<stbrp_node> packNodes_;
|
||||
|
||||
// 预分配缓冲区,避免每次动态分配
|
||||
mutable std::vector<uint8_t> glyphBitmapCache_;
|
||||
mutable std::vector<uint8_t> glyphRgbaCache_;
|
||||
|
||||
// 初始化字体
|
||||
bool initFont(const std::string& filepath);
|
||||
// 创建空白图集纹理
|
||||
void createAtlas();
|
||||
// 缓存字形到图集
|
||||
void cacheGlyph(char32_t codepoint);
|
||||
// 更新图集纹理区域
|
||||
void updateAtlas(int x, int y, int width, int height,
|
||||
const std::vector<uint8_t>& data);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/resources/framebuffer.h>
|
||||
#include <glad/glad.h>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 帧缓冲实现
|
||||
// ============================================================================
|
||||
class GLFramebuffer : public Framebuffer {
|
||||
public:
|
||||
// 最大颜色附件数
|
||||
static constexpr int MAX_COLOR_ATTACHMENTS = 8;
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLFramebuffer();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLFramebuffer() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化帧缓冲
|
||||
* @param desc 帧缓冲描述
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(const FramebufferDesc& desc);
|
||||
|
||||
/**
|
||||
* @brief 关闭帧缓冲,释放资源
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
// Framebuffer 接口实现
|
||||
void bind() override;
|
||||
void unbind() override;
|
||||
void attachColorTexture(Ptr<Texture> texture, int attachment = 0) override;
|
||||
void attachDepthTexture(Ptr<Texture> texture) override;
|
||||
void attachDepthStencilTexture(Ptr<Texture> texture) override;
|
||||
bool isComplete() override;
|
||||
Ptr<Texture> getColorTexture(int attachment = 0) const override;
|
||||
Ptr<Texture> getDepthTexture() const override;
|
||||
int getWidth() const override { return width_; }
|
||||
int getHeight() const override { return height_; }
|
||||
Size getSize() const override { return Size(static_cast<float>(width_), static_cast<float>(height_)); }
|
||||
bool isValid() const override { return fboID_ != 0; }
|
||||
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(fboID_); }
|
||||
void clear(const Color& color, bool clearColor = true,
|
||||
bool clearDepth = true, bool clearStencil = false) override;
|
||||
void setViewport(int x, int y, int width, int height) override;
|
||||
bool readPixels(int x, int y, int width, int height,
|
||||
std::vector<uint8_t>& outData) override;
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL FBO ID
|
||||
* @return FBO ID
|
||||
*/
|
||||
GLuint getFboID() const { return fboID_; }
|
||||
|
||||
/**
|
||||
* @brief 创建带内置纹理的帧缓冲(便捷方法)
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param colorFormat 颜色格式
|
||||
* @param depthFormat 深度格式(可选)
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool createWithTextures(int width, int height,
|
||||
PixelFormat colorFormat = PixelFormat::RGBA8,
|
||||
PixelFormat depthFormat = PixelFormat::Depth24);
|
||||
|
||||
private:
|
||||
GLuint fboID_ = 0;
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
int numColorAttachments_ = 1;
|
||||
bool hasDepth_ = false;
|
||||
bool hasStencil_ = false;
|
||||
|
||||
// 附件纹理
|
||||
std::array<Ptr<Texture>, MAX_COLOR_ATTACHMENTS> colorTextures_;
|
||||
Ptr<Texture> depthTexture_;
|
||||
Ptr<Texture> depthStencilTexture_;
|
||||
|
||||
// 是否为内置纹理(需要自动清理)
|
||||
bool hasInternalTextures_ = false;
|
||||
|
||||
/**
|
||||
* @brief 检查并更新完整状态
|
||||
*/
|
||||
bool checkStatus();
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 附件枚举
|
||||
*/
|
||||
static GLenum getColorAttachment(int index);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/resources/pipeline.h>
|
||||
#include <glad/glad.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 管线状态实现
|
||||
// ============================================================================
|
||||
class GLPipeline : public Pipeline {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLPipeline();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLPipeline() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化管线
|
||||
* @param desc 管线描述
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool init(const PipelineDesc& desc);
|
||||
|
||||
/**
|
||||
* @brief 关闭管线,释放资源
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
// Pipeline 接口实现
|
||||
void bind() override;
|
||||
void unbind() override;
|
||||
void setBlendMode(BlendMode mode) override;
|
||||
BlendMode getBlendMode() const override { return blendMode_; }
|
||||
void setDepthTest(bool enabled) override;
|
||||
void setDepthWrite(bool enabled) override;
|
||||
void setDepthFunc(DepthFunc func) override;
|
||||
void setCullMode(CullMode mode) override;
|
||||
bool isValid() const override { return initialized_; }
|
||||
uintptr_t getNativeHandle() const override { return 0; } // OpenGL 管线没有单一句柄
|
||||
|
||||
/**
|
||||
* @brief 设置视口
|
||||
* @param x 视口左下角X坐标
|
||||
* @param y 视口左下角Y坐标
|
||||
* @param width 视口宽度
|
||||
* @param height 视口高度
|
||||
*/
|
||||
void setViewport(int x, int y, int width, int height);
|
||||
|
||||
/**
|
||||
* @brief 获取当前视口
|
||||
* @param x 输出X坐标
|
||||
* @param y 输出Y坐标
|
||||
* @param width 输出宽度
|
||||
* @param height 输出高度
|
||||
*/
|
||||
void getViewport(int& x, int& y, int& width, int& height) const;
|
||||
|
||||
/**
|
||||
* @brief 应用所有状态(用于初始化或重置)
|
||||
*/
|
||||
void applyAllStates();
|
||||
|
||||
private:
|
||||
bool initialized_ = false;
|
||||
|
||||
// 当前状态
|
||||
BlendMode blendMode_ = BlendMode::Alpha;
|
||||
bool blendEnabled_ = true;
|
||||
bool depthTest_ = false;
|
||||
bool depthWrite_ = false;
|
||||
DepthFunc depthFunc_ = DepthFunc::Less;
|
||||
CullMode cullMode_ = CullMode::None;
|
||||
|
||||
// 视口
|
||||
int viewportX_ = 0;
|
||||
int viewportY_ = 0;
|
||||
int viewportWidth_ = 0;
|
||||
int viewportHeight_ = 0;
|
||||
|
||||
// 状态缓存(避免冗余 GL 调用)
|
||||
BlendMode cachedBlendMode_ = BlendMode::None;
|
||||
bool cachedBlendEnabled_ = false;
|
||||
bool cachedDepthTest_ = false;
|
||||
bool cachedDepthWrite_ = false;
|
||||
DepthFunc cachedDepthFunc_ = DepthFunc::Less;
|
||||
CullMode cachedCullMode_ = CullMode::None;
|
||||
int cachedViewportX_ = -1;
|
||||
int cachedViewportY_ = -1;
|
||||
int cachedViewportWidth_ = -1;
|
||||
int cachedViewportHeight_ = -1;
|
||||
|
||||
/**
|
||||
* @brief 应用混合状态
|
||||
*/
|
||||
void applyBlendState();
|
||||
|
||||
/**
|
||||
* @brief 应用深度状态
|
||||
*/
|
||||
void applyDepthState();
|
||||
|
||||
/**
|
||||
* @brief 应用裁剪状态
|
||||
*/
|
||||
void applyCullState();
|
||||
|
||||
/**
|
||||
* @brief 转换混合模式到 OpenGL 枚举
|
||||
*/
|
||||
static void getBlendFactors(BlendMode mode, GLenum& srcFactor, GLenum& dstFactor);
|
||||
|
||||
/**
|
||||
* @brief 转换深度函数到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertDepthFunc(DepthFunc func);
|
||||
|
||||
/**
|
||||
* @brief 转换裁剪模式到 OpenGL 枚举
|
||||
*/
|
||||
static GLenum convertCullMode(CullMode mode);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/opengl/gl_shader.h>
|
||||
#include <extra2d/graphics/opengl/gl_sprite_batch.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_buffer.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_framebuffer.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_pipeline.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_sprite_batch.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/graphics/shader/shader_interface.h>
|
||||
|
||||
#include <array>
|
||||
#include <glad/glad.h>
|
||||
|
|
@ -10,7 +13,10 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
class Window;
|
||||
// 前向声明
|
||||
class IWindow;
|
||||
class GLContext;
|
||||
class GLFramebuffer;
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 渲染器实现
|
||||
|
|
@ -21,7 +27,7 @@ public:
|
|||
~GLRenderer() override;
|
||||
|
||||
// RenderBackend 接口实现
|
||||
bool init(Window *window) override;
|
||||
bool init(IWindow* window) override;
|
||||
void shutdown() override;
|
||||
|
||||
void beginFrame(const Color &clearColor) override;
|
||||
|
|
@ -48,6 +54,7 @@ public:
|
|||
void drawSprite(const Texture &texture, const Vec2 &position,
|
||||
const Color &tint) override;
|
||||
void endSpriteBatch() override;
|
||||
void flush() override;
|
||||
|
||||
void drawLine(const Vec2 &start, const Vec2 &end, const Color &color,
|
||||
float width) override;
|
||||
|
|
@ -76,6 +83,42 @@ public:
|
|||
Stats getStats() const override { return stats_; }
|
||||
void resetStats() override;
|
||||
|
||||
// GLFramebuffer 相关方法
|
||||
|
||||
/**
|
||||
* @brief 创建帧缓冲对象
|
||||
* @param desc 帧缓冲描述
|
||||
* @return 创建的帧缓冲智能指针
|
||||
*/
|
||||
Ptr<GLFramebuffer> createFramebuffer(const FramebufferDesc& desc);
|
||||
|
||||
/**
|
||||
* @brief 绑定帧缓冲(作为渲染目标)
|
||||
* @param framebuffer 帧缓冲对象指针,传入 nullptr 则绑定默认帧缓冲
|
||||
*/
|
||||
void bindFramebuffer(GLFramebuffer* framebuffer);
|
||||
|
||||
/**
|
||||
* @brief 解绑帧缓冲(恢复到默认帧缓冲)
|
||||
*/
|
||||
void unbindFramebuffer();
|
||||
|
||||
/**
|
||||
* @brief 获取默认帧缓冲
|
||||
* @return 默认帧缓冲智能指针
|
||||
*/
|
||||
Ptr<GLFramebuffer> getDefaultFramebuffer() const;
|
||||
|
||||
/**
|
||||
* @brief 清除当前绑定的帧缓冲
|
||||
* @param color 清除颜色
|
||||
* @param clearColor 是否清除颜色缓冲
|
||||
* @param clearDepth 是否清除深度缓冲
|
||||
* @param clearStencil 是否清除模板缓冲
|
||||
*/
|
||||
void clearFramebuffer(const Color& color, bool clearColor = true,
|
||||
bool clearDepth = true, bool clearStencil = false);
|
||||
|
||||
private:
|
||||
// 形状批处理常量
|
||||
static constexpr size_t MAX_CIRCLE_SEGMENTS = 128;
|
||||
|
|
@ -88,14 +131,14 @@ private:
|
|||
float r, g, b, a;
|
||||
};
|
||||
|
||||
Window *window_;
|
||||
IWindow* window_;
|
||||
GLSpriteBatch spriteBatch_;
|
||||
GLShader shapeShader_;
|
||||
Ptr<IShader> shapeShader_;
|
||||
|
||||
GLuint shapeVao_;
|
||||
GLuint shapeVbo_;
|
||||
GLuint lineVao_; // 线条专用 VAO
|
||||
GLuint lineVbo_; // 线条专用 VBO
|
||||
GLuint shapeVao_; // 形状 VAO(手动管理,用于顶点属性配置)
|
||||
GLBuffer shapeBuffer_; // 形状 VBO(使用 GLBuffer 管理)
|
||||
GLuint lineVao_; // 线条 VAO(手动管理,用于顶点属性配置)
|
||||
GLBuffer lineBuffer_; // 线条 VBO(使用 GLBuffer 管理)
|
||||
|
||||
glm::mat4 viewProjection_;
|
||||
std::vector<glm::mat4> transformStack_;
|
||||
|
|
@ -112,15 +155,23 @@ private:
|
|||
size_t lineVertexCount_ = 0;
|
||||
float currentLineWidth_ = 1.0f;
|
||||
|
||||
// OpenGL 状态缓存
|
||||
BlendMode cachedBlendMode_ = BlendMode::None;
|
||||
bool blendEnabled_ = false;
|
||||
int cachedViewportX_ = 0;
|
||||
int cachedViewportY_ = 0;
|
||||
int cachedViewportWidth_ = 0;
|
||||
int cachedViewportHeight_ = 0;
|
||||
// OpenGL 管线状态管理
|
||||
GLPipeline pipeline_;
|
||||
|
||||
// 自动批处理状态
|
||||
bool batchActive_ = false; // 批处理是否激活
|
||||
bool autoBatchEnabled_ = true; // 是否启用自动批处理
|
||||
const Texture* currentBatchTexture_ = nullptr; // 当前批处理的纹理
|
||||
std::vector<SpriteData> pendingSprites_; // 待提交的精灵
|
||||
static constexpr size_t MAX_BATCH_SPRITES = 1000; // 最大批处理精灵数
|
||||
|
||||
// 帧缓冲管理
|
||||
mutable Ptr<GLFramebuffer> defaultFramebuffer_; // 默认帧缓冲(延迟创建)
|
||||
GLFramebuffer* currentFramebuffer_ = nullptr; // 当前绑定的帧缓冲
|
||||
|
||||
void initShapeRendering();
|
||||
void ensureBatchActive(); // 确保批处理已激活
|
||||
void submitPendingSprites(); // 提交待处理的精灵
|
||||
void flushShapeBatch();
|
||||
void flushLineBatch();
|
||||
void addShapeVertex(float x, float y, const Color &color);
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/graphics/shader/shader_interface.h>
|
||||
#include <glad/glad.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class GLShader : public IShader {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLShader();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLShader() override;
|
||||
|
||||
/**
|
||||
* @brief 绑定Shader程序
|
||||
*/
|
||||
void bind() const override;
|
||||
|
||||
/**
|
||||
* @brief 解绑Shader程序
|
||||
*/
|
||||
void unbind() const override;
|
||||
|
||||
/**
|
||||
* @brief 设置布尔类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 布尔值
|
||||
*/
|
||||
void setBool(const std::string& name, bool value) override;
|
||||
|
||||
/**
|
||||
* @brief 设置整数类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 整数值
|
||||
*/
|
||||
void setInt(const std::string& name, int value) override;
|
||||
|
||||
/**
|
||||
* @brief 设置浮点类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 浮点值
|
||||
*/
|
||||
void setFloat(const std::string& name, float value) override;
|
||||
|
||||
/**
|
||||
* @brief 设置二维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 二维向量值
|
||||
*/
|
||||
void setVec2(const std::string& name, const glm::vec2& value) override;
|
||||
|
||||
/**
|
||||
* @brief 设置三维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 三维向量值
|
||||
*/
|
||||
void setVec3(const std::string& name, const glm::vec3& value) override;
|
||||
|
||||
/**
|
||||
* @brief 设置四维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 四维向量值
|
||||
*/
|
||||
void setVec4(const std::string& name, const glm::vec4& value) override;
|
||||
|
||||
/**
|
||||
* @brief 设置4x4矩阵类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 4x4矩阵值
|
||||
*/
|
||||
void setMat4(const std::string& name, const glm::mat4& value) override;
|
||||
|
||||
/**
|
||||
* @brief 设置颜色类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param color 颜色值
|
||||
*/
|
||||
void setColor(const std::string& name, const Color& color) override;
|
||||
|
||||
/**
|
||||
* @brief 检查Shader是否有效
|
||||
* @return 有效返回true,否则返回false
|
||||
*/
|
||||
bool isValid() const override { return programID_ != 0; }
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄(OpenGL程序ID)
|
||||
* @return OpenGL程序ID
|
||||
*/
|
||||
uint32_t getNativeHandle() const override { return programID_; }
|
||||
|
||||
/**
|
||||
* @brief 获取Shader名称
|
||||
* @return Shader名称
|
||||
*/
|
||||
const std::string& getName() const override { return name_; }
|
||||
|
||||
/**
|
||||
* @brief 设置Shader名称
|
||||
* @param name Shader名称
|
||||
*/
|
||||
void setName(const std::string& name) override { name_ = name; }
|
||||
|
||||
/**
|
||||
* @brief 从源码编译Shader
|
||||
* @param vertexSource 顶点着色器源码
|
||||
* @param fragmentSource 片段着色器源码
|
||||
* @return 编译成功返回true,失败返回false
|
||||
*/
|
||||
bool compileFromSource(const char* vertexSource, const char* fragmentSource);
|
||||
|
||||
/**
|
||||
* @brief 从二进制数据创建Shader
|
||||
* @param binary 二进制数据
|
||||
* @return 创建成功返回true,失败返回false
|
||||
*/
|
||||
bool compileFromBinary(const std::vector<uint8_t>& binary);
|
||||
|
||||
/**
|
||||
* @brief 获取Shader二进制数据
|
||||
* @param outBinary 输出的二进制数据
|
||||
* @return 成功返回true,失败返回false
|
||||
*/
|
||||
bool getBinary(std::vector<uint8_t>& outBinary);
|
||||
|
||||
/**
|
||||
* @brief 获取OpenGL程序ID
|
||||
* @return OpenGL程序ID
|
||||
*/
|
||||
GLuint getProgramID() const { return programID_; }
|
||||
|
||||
private:
|
||||
GLuint programID_ = 0;
|
||||
std::string name_;
|
||||
std::unordered_map<std::string, GLint> uniformCache_;
|
||||
|
||||
/**
|
||||
* @brief 编译单个着色器
|
||||
* @param type 着色器类型
|
||||
* @param source 着色器源码
|
||||
* @return 着色器ID,失败返回0
|
||||
*/
|
||||
GLuint compileShader(GLenum type, const char* source);
|
||||
|
||||
/**
|
||||
* @brief 获取uniform位置
|
||||
* @param name uniform变量名
|
||||
* @return uniform位置
|
||||
*/
|
||||
GLint getUniformLocation(const std::string& name);
|
||||
};
|
||||
|
||||
class GLShaderFactory : public IShaderFactory {
|
||||
public:
|
||||
/**
|
||||
* @brief 从源码创建Shader
|
||||
* @param name Shader名称
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 创建的Shader实例
|
||||
*/
|
||||
Ptr<IShader> createFromSource(
|
||||
const std::string& name,
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource) override;
|
||||
|
||||
/**
|
||||
* @brief 从缓存二进制创建Shader
|
||||
* @param name Shader名称
|
||||
* @param binary 编译后的二进制数据
|
||||
* @return 创建的Shader实例
|
||||
*/
|
||||
Ptr<IShader> createFromBinary(
|
||||
const std::string& name,
|
||||
const std::vector<uint8_t>& binary) override;
|
||||
|
||||
/**
|
||||
* @brief 获取Shader的二进制数据
|
||||
* @param shader Shader实例
|
||||
* @param outBinary 输出的二进制数据
|
||||
* @return 成功返回true,失败返回false
|
||||
*/
|
||||
bool getShaderBinary(const IShader& shader,
|
||||
std::vector<uint8_t>& outBinary) override;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/backends/opengl/gl_buffer.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/batch/sprite_batch.h>
|
||||
#include <extra2d/graphics/shader/shader_interface.h>
|
||||
|
||||
#include <array>
|
||||
#include <glad/glad.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 精灵批处理渲染器
|
||||
// 使用 batch/sprite_batch 作为后端无关的批处理层
|
||||
// ============================================================================
|
||||
class GLSpriteBatch {
|
||||
public:
|
||||
GLSpriteBatch();
|
||||
~GLSpriteBatch();
|
||||
|
||||
// 初始化/关闭
|
||||
bool init();
|
||||
void shutdown();
|
||||
|
||||
// 批处理生命周期
|
||||
void begin(const glm::mat4& viewProjection);
|
||||
void end();
|
||||
|
||||
// 绘制单个精灵
|
||||
void draw(const Texture& texture, const SpriteData& data);
|
||||
|
||||
// 批量绘制(用于文本渲染优化)
|
||||
void drawBatch(const Texture& texture, const std::vector<SpriteData>& sprites);
|
||||
|
||||
// 获取绘制调用次数
|
||||
uint32_t getDrawCallCount() const { return drawCallCount_; }
|
||||
|
||||
private:
|
||||
// OpenGL 对象
|
||||
GLuint vao_;
|
||||
GLBuffer vbo_; // 顶点缓冲区(动态)
|
||||
GLBuffer ebo_; // 索引缓冲区(静态)
|
||||
|
||||
// 后端无关的批处理层
|
||||
SpriteBatch batch_;
|
||||
|
||||
// 批次管理
|
||||
struct Batch {
|
||||
const GLTexture* texture;
|
||||
size_t startVertex;
|
||||
size_t vertexCount;
|
||||
};
|
||||
std::vector<Batch> batches_;
|
||||
const GLTexture* currentTexture_;
|
||||
|
||||
// 着色器和矩阵
|
||||
Ptr<IShader> shader_;
|
||||
uint32_t drawCallCount_;
|
||||
glm::mat4 viewProjection_;
|
||||
|
||||
// 内部方法
|
||||
void flush();
|
||||
void submitBatch();
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/alpha_mask.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/graphics/texture/alpha_mask.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief Vulkan 渲染器实现(占位)
|
||||
*
|
||||
* 这是一个占位实现,用于展示Vulkan后端应该包含的内容。
|
||||
* 完整的实现需要包含Vulkan上下文、设备、交换链、管线等。
|
||||
*/
|
||||
class VulkanRenderer : public RenderBackend {
|
||||
public:
|
||||
VulkanRenderer();
|
||||
~VulkanRenderer() override;
|
||||
|
||||
// RenderBackend 接口实现
|
||||
bool init(IWindow* window) override;
|
||||
void shutdown() override;
|
||||
|
||||
void beginFrame(const Color &clearColor) override;
|
||||
void endFrame() override;
|
||||
void setViewport(int x, int y, int width, int height) override;
|
||||
void setVSync(bool enabled) override;
|
||||
|
||||
void setBlendMode(BlendMode mode) override;
|
||||
void setViewProjection(const glm::mat4 &matrix) override;
|
||||
|
||||
void pushTransform(const glm::mat4 &transform) override;
|
||||
void popTransform() override;
|
||||
glm::mat4 getCurrentTransform() const override;
|
||||
|
||||
Ptr<Texture> createTexture(int width, int height, const uint8_t *pixels,
|
||||
int channels) override;
|
||||
Ptr<Texture> loadTexture(const std::string &filepath) override;
|
||||
|
||||
void beginSpriteBatch() override;
|
||||
void drawSprite(const Texture &texture, const Rect &destRect,
|
||||
const Rect &srcRect, const Color &tint, float rotation,
|
||||
const Vec2 &anchor) override;
|
||||
void drawSprite(const Texture &texture, const Vec2 &position,
|
||||
const Color &tint) override;
|
||||
void endSpriteBatch() override;
|
||||
|
||||
void drawLine(const Vec2 &start, const Vec2 &end, const Color &color,
|
||||
float width) override;
|
||||
void drawRect(const Rect &rect, const Color &color, float width) override;
|
||||
void fillRect(const Rect &rect, const Color &color) override;
|
||||
void drawCircle(const Vec2 ¢er, float radius, const Color &color,
|
||||
int segments, float width) override;
|
||||
void fillCircle(const Vec2 ¢er, float radius, const Color &color,
|
||||
int segments) override;
|
||||
void drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color, float width) override;
|
||||
void fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color) override;
|
||||
void drawPolygon(const std::vector<Vec2> &points, const Color &color,
|
||||
float width) override;
|
||||
void fillPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color) override;
|
||||
|
||||
Ptr<FontAtlas> createFontAtlas(const std::string &filepath, int fontSize,
|
||||
bool useSDF = false) override;
|
||||
void drawText(const FontAtlas &font, const std::string &text,
|
||||
const Vec2 &position, const Color &color) override;
|
||||
void drawText(const FontAtlas &font, const std::string &text, float x,
|
||||
float y, const Color &color) override;
|
||||
|
||||
Stats getStats() const override { return stats_; }
|
||||
void resetStats() override;
|
||||
|
||||
private:
|
||||
Stats stats_;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 形状顶点结构
|
||||
// ============================================================================
|
||||
struct ShapeVertex {
|
||||
float x, y; // 位置
|
||||
float r, g, b, a; // 颜色
|
||||
|
||||
ShapeVertex() = default;
|
||||
ShapeVertex(float px, float py, const Color& c)
|
||||
: x(px), y(py), r(c.r), g(c.g), b(c.b), a(c.a) {}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 形状批处理抽象接口 - 后端无关
|
||||
// ============================================================================
|
||||
class ShapeBatch {
|
||||
public:
|
||||
virtual ~ShapeBatch() = default;
|
||||
|
||||
/**
|
||||
* @brief 初始化形状批处理
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
virtual bool init() = 0;
|
||||
|
||||
/**
|
||||
* @brief 关闭形状批处理,释放资源
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/**
|
||||
* @brief 开始批处理
|
||||
* @param viewProjection 视图投影矩阵
|
||||
*/
|
||||
virtual void begin(const glm::mat4& viewProjection) = 0;
|
||||
|
||||
/**
|
||||
* @brief 结束批处理并提交绘制
|
||||
*/
|
||||
virtual void end() = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制线段
|
||||
* @param start 起点
|
||||
* @param end 终点
|
||||
* @param color 颜色
|
||||
* @param width 线宽
|
||||
*/
|
||||
virtual void drawLine(const Vec2& start, const Vec2& end,
|
||||
const Color& color, float width = 1.0f) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制矩形边框
|
||||
* @param rect 矩形区域
|
||||
* @param color 颜色
|
||||
* @param width 边框宽度
|
||||
*/
|
||||
virtual void drawRect(const Rect& rect, const Color& color,
|
||||
float width = 1.0f) = 0;
|
||||
|
||||
/**
|
||||
* @brief 填充矩形
|
||||
* @param rect 矩形区域
|
||||
* @param color 颜色
|
||||
*/
|
||||
virtual void fillRect(const Rect& rect, const Color& color) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制圆形边框
|
||||
* @param center 圆心
|
||||
* @param radius 半径
|
||||
* @param color 颜色
|
||||
* @param segments 分段数
|
||||
* @param width 边框宽度
|
||||
*/
|
||||
virtual void drawCircle(const Vec2& center, float radius,
|
||||
const Color& color, int segments = 32,
|
||||
float width = 1.0f) = 0;
|
||||
|
||||
/**
|
||||
* @brief 填充圆形
|
||||
* @param center 圆心
|
||||
* @param radius 半径
|
||||
* @param color 颜色
|
||||
* @param segments 分段数
|
||||
*/
|
||||
virtual void fillCircle(const Vec2& center, float radius,
|
||||
const Color& color, int segments = 32) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制三角形边框
|
||||
* @param p1 顶点1
|
||||
* @param p2 顶点2
|
||||
* @param p3 顶点3
|
||||
* @param color 颜色
|
||||
* @param width 边框宽度
|
||||
*/
|
||||
virtual void drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3,
|
||||
const Color& color, float width = 1.0f) = 0;
|
||||
|
||||
/**
|
||||
* @brief 填充三角形
|
||||
* @param p1 顶点1
|
||||
* @param p2 顶点2
|
||||
* @param p3 顶点3
|
||||
* @param color 颜色
|
||||
*/
|
||||
virtual void fillTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3,
|
||||
const Color& color) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制多边形边框
|
||||
* @param points 顶点数组
|
||||
* @param color 颜色
|
||||
* @param width 边框宽度
|
||||
*/
|
||||
virtual void drawPolygon(const std::vector<Vec2>& points,
|
||||
const Color& color, float width = 1.0f) = 0;
|
||||
|
||||
/**
|
||||
* @brief 填充多边形
|
||||
* @param points 顶点数组
|
||||
* @param color 颜色
|
||||
*/
|
||||
virtual void fillPolygon(const std::vector<Vec2>& points,
|
||||
const Color& color) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取绘制调用次数
|
||||
* @return 绘制调用次数
|
||||
*/
|
||||
virtual uint32_t getDrawCallCount() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 重置绘制调用计数
|
||||
*/
|
||||
virtual void resetDrawCallCount() = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 三角函数查表 - 避免每帧计算 sin/cos
|
||||
// ============================================================================
|
||||
class TrigLookup {
|
||||
public:
|
||||
TrigLookup();
|
||||
|
||||
// 通过角度(0-360)获取 sin/cos
|
||||
float sin(int angle) const;
|
||||
float cos(int angle) const;
|
||||
|
||||
// 通过弧度获取 sin/cos
|
||||
float sinRad(float rad) const;
|
||||
float cosRad(float rad) const;
|
||||
|
||||
private:
|
||||
static constexpr int TABLE_SIZE = 360 * 4; // 0.25度精度
|
||||
std::array<float, TABLE_SIZE> sinTable_;
|
||||
std::array<float, TABLE_SIZE> cosTable_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 精灵批次数据 - 后端无关
|
||||
// ============================================================================
|
||||
struct SpriteVertex {
|
||||
Vec2 position;
|
||||
Vec2 texCoord;
|
||||
Color color;
|
||||
};
|
||||
|
||||
struct SpriteData {
|
||||
Vec2 position;
|
||||
Vec2 size;
|
||||
float rotation;
|
||||
Vec2 pivot;
|
||||
Color color;
|
||||
const Texture* texture;
|
||||
Rect uvRect;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 通用精灵批处理 - 后端无关
|
||||
// 负责:顶点生成、批次管理、三角函数查表
|
||||
// ============================================================================
|
||||
class SpriteBatch {
|
||||
public:
|
||||
static constexpr size_t MAX_SPRITES = 10000;
|
||||
static constexpr size_t VERTICES_PER_SPRITE = 4;
|
||||
static constexpr size_t INDICES_PER_SPRITE = 6;
|
||||
static constexpr size_t MAX_VERTICES = MAX_SPRITES * VERTICES_PER_SPRITE;
|
||||
static constexpr size_t MAX_INDICES = MAX_SPRITES * INDICES_PER_SPRITE;
|
||||
|
||||
SpriteBatch();
|
||||
~SpriteBatch() = default;
|
||||
|
||||
// 开始批次
|
||||
void begin(const glm::mat4& viewProjection);
|
||||
|
||||
// 结束批次 - 返回需要绘制的批次列表
|
||||
void end();
|
||||
|
||||
// 绘制单个精灵
|
||||
void draw(const SpriteData& sprite);
|
||||
|
||||
// 批量绘制 - 一次性处理多个精灵
|
||||
void drawBatch(const std::vector<SpriteData>& sprites);
|
||||
|
||||
// 立即绘制 - 不缓存,直接提交
|
||||
void drawImmediate(const SpriteData& sprite);
|
||||
|
||||
// 获取当前批次数据
|
||||
const std::vector<SpriteVertex>& getVertices() const { return vertices_; }
|
||||
const std::vector<uint16_t>& getIndices() const { return indices_; }
|
||||
size_t getSpriteCount() const { return spriteCount_; }
|
||||
|
||||
// 检查是否需要刷新
|
||||
bool needsFlush() const { return spriteCount_ >= MAX_SPRITES; }
|
||||
|
||||
// 清空批次
|
||||
void clear();
|
||||
|
||||
private:
|
||||
// 三角函数查表
|
||||
TrigLookup trigLookup_;
|
||||
|
||||
// 顶点数据 - 使用固定大小数组避免动态分配
|
||||
std::vector<SpriteVertex> vertices_;
|
||||
std::vector<uint16_t> indices_;
|
||||
size_t spriteCount_;
|
||||
|
||||
// 变换矩阵
|
||||
glm::mat4 viewProjection_;
|
||||
glm::mat4 cachedVP_;
|
||||
bool vpDirty_;
|
||||
|
||||
// 生成索引
|
||||
void generateIndices();
|
||||
|
||||
// 生成顶点
|
||||
void generateVertices(const SpriteData& sprite, size_t vertexOffset);
|
||||
|
||||
// 刷新批次
|
||||
void flush();
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
class ViewportAdapter;
|
||||
|
||||
// ============================================================================
|
||||
// 2D 正交相机
|
||||
// ============================================================================
|
||||
|
|
@ -20,8 +22,8 @@ public:
|
|||
// ------------------------------------------------------------------------
|
||||
// 位置和变换
|
||||
// ------------------------------------------------------------------------
|
||||
void setPosition(const Vec2 &position);
|
||||
void setPosition(float x, float y);
|
||||
void setPos(const Vec2 &position);
|
||||
void setPos(float x, float y);
|
||||
Vec2 getPosition() const { return position_; }
|
||||
|
||||
void setRotation(float degrees);
|
||||
|
|
@ -65,6 +67,20 @@ public:
|
|||
void clearBounds();
|
||||
void clampToBounds();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 视口适配器
|
||||
// ------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief 设置视口适配器
|
||||
* @param adapter 视口适配器指针
|
||||
*/
|
||||
void setViewportAdapter(ViewportAdapter *adapter);
|
||||
|
||||
/**
|
||||
* @brief 根据视口适配器自动设置视口
|
||||
*/
|
||||
void applyViewportAdapter();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 快捷方法:看向某点
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -83,6 +99,8 @@ private:
|
|||
Rect bounds_;
|
||||
bool hasBounds_ = false;
|
||||
|
||||
ViewportAdapter *viewportAdapter_ = nullptr;
|
||||
|
||||
mutable glm::mat4 viewMatrix_;
|
||||
mutable glm::mat4 projMatrix_;
|
||||
mutable glm::mat4 vpMatrix_;
|
||||
|
|
@ -0,0 +1,332 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 视口适配模式枚举
|
||||
// ============================================================================
|
||||
enum class ViewportMode {
|
||||
AspectRatio,
|
||||
Stretch,
|
||||
Center,
|
||||
Custom
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 黑边位置枚举
|
||||
// ============================================================================
|
||||
enum class LetterboxPosition {
|
||||
Center,
|
||||
LeftTop,
|
||||
RightTop,
|
||||
LeftBottom,
|
||||
RightBottom
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 视口配置结构体
|
||||
// ============================================================================
|
||||
struct ViewportConfig {
|
||||
float logicWidth = 1920.0f;
|
||||
float logicHeight = 1080.0f;
|
||||
ViewportMode mode = ViewportMode::AspectRatio;
|
||||
LetterboxPosition letterboxPosition = LetterboxPosition::Center;
|
||||
Color letterboxColor = Colors::Black;
|
||||
bool autoScaleInCenterMode = true;
|
||||
float customScale = 1.0f;
|
||||
Vec2 customOffset = Vec2::Zero();
|
||||
Rect customViewport = Rect::Zero();
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 视口计算结果结构体
|
||||
// ============================================================================
|
||||
struct ViewportResult {
|
||||
Rect viewport;
|
||||
float scaleX = 1.0f;
|
||||
float scaleY = 1.0f;
|
||||
float uniformScale = 1.0f;
|
||||
Vec2 offset;
|
||||
bool hasLetterbox = false;
|
||||
|
||||
struct Letterbox {
|
||||
Rect top;
|
||||
Rect bottom;
|
||||
Rect left;
|
||||
Rect right;
|
||||
} letterbox;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 视口适配器类
|
||||
// ============================================================================
|
||||
class ViewportAdapter {
|
||||
public:
|
||||
ViewportAdapter();
|
||||
ViewportAdapter(float logicWidth, float logicHeight);
|
||||
~ViewportAdapter() = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 配置设置
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置视口配置
|
||||
* @param config 视口配置结构体
|
||||
*/
|
||||
void setConfig(const ViewportConfig &config);
|
||||
|
||||
/**
|
||||
* @brief 获取当前视口配置
|
||||
* @return 当前视口配置
|
||||
*/
|
||||
const ViewportConfig &getConfig() const { return config_; }
|
||||
|
||||
/**
|
||||
* @brief 设置逻辑分辨率
|
||||
* @param width 逻辑宽度
|
||||
* @param height 逻辑高度
|
||||
*/
|
||||
void setLogicSize(float width, float height);
|
||||
|
||||
/**
|
||||
* @brief 设置视口适配模式
|
||||
* @param mode 适配模式
|
||||
*/
|
||||
void setMode(ViewportMode mode);
|
||||
|
||||
/**
|
||||
* @brief 设置黑边位置
|
||||
* @param position 黑边位置
|
||||
*/
|
||||
void setLetterboxPosition(LetterboxPosition position);
|
||||
|
||||
/**
|
||||
* @brief 设置黑边颜色
|
||||
* @param color 黑边颜色
|
||||
*/
|
||||
void setLetterboxColor(const Color &color);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 更新和计算
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 更新视口适配计算
|
||||
* @param screenWidth 屏幕宽度
|
||||
* @param screenHeight 屏幕高度
|
||||
*/
|
||||
void update(int screenWidth, int screenHeight);
|
||||
|
||||
/**
|
||||
* @brief 获取计算结果
|
||||
* @return 视口计算结果
|
||||
*/
|
||||
const ViewportResult &getResult() const { return result_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 坐标转换
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 屏幕坐标转逻辑坐标
|
||||
* @param screenPos 屏幕坐标
|
||||
* @return 逻辑坐标
|
||||
*/
|
||||
Vec2 screenToLogic(const Vec2 &screenPos) const;
|
||||
|
||||
/**
|
||||
* @brief 逻辑坐标转屏幕坐标
|
||||
* @param logicPos 逻辑坐标
|
||||
* @return 屏幕坐标
|
||||
*/
|
||||
Vec2 logicToScreen(const Vec2 &logicPos) const;
|
||||
|
||||
/**
|
||||
* @brief 屏幕坐标转逻辑坐标(分量形式)
|
||||
* @param x 屏幕X坐标
|
||||
* @param y 屏幕Y坐标
|
||||
* @return 逻辑坐标
|
||||
*/
|
||||
Vec2 screenToLogic(float x, float y) const;
|
||||
|
||||
/**
|
||||
* @brief 逻辑坐标转屏幕坐标(分量形式)
|
||||
* @param x 逻辑X坐标
|
||||
* @param y 逻辑Y坐标
|
||||
* @return 屏幕坐标
|
||||
*/
|
||||
Vec2 logicToScreen(float x, float y) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 矩阵获取
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取视口变换矩阵
|
||||
* @return 视口变换矩阵(从逻辑坐标到屏幕坐标)
|
||||
*/
|
||||
glm::mat4 getMatrix() const;
|
||||
|
||||
/**
|
||||
* @brief 获取反向视口变换矩阵
|
||||
* @return 反向视口变换矩阵(从屏幕坐标到逻辑坐标)
|
||||
*/
|
||||
glm::mat4 getInvMatrix() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 区域检测
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 检查屏幕坐标是否在视口内
|
||||
* @param screenPos 屏幕坐标
|
||||
* @return 如果在视口内返回 true
|
||||
*/
|
||||
bool isInViewport(const Vec2 &screenPos) const;
|
||||
|
||||
/**
|
||||
* @brief 检查屏幕坐标是否在黑边区域
|
||||
* @param screenPos 屏幕坐标
|
||||
* @return 如果在黑边区域返回 true
|
||||
*/
|
||||
bool isInLetterbox(const Vec2 &screenPos) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Getter 方法
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取逻辑宽度
|
||||
* @return 逻辑宽度
|
||||
*/
|
||||
float getLogicWidth() const { return config_.logicWidth; }
|
||||
|
||||
/**
|
||||
* @brief 获取逻辑高度
|
||||
* @return 逻辑高度
|
||||
*/
|
||||
float getLogicHeight() const { return config_.logicHeight; }
|
||||
|
||||
/**
|
||||
* @brief 获取逻辑尺寸
|
||||
* @return 逻辑尺寸
|
||||
*/
|
||||
Size getLogicSize() const {
|
||||
return Size(config_.logicWidth, config_.logicHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取屏幕宽度
|
||||
* @return 屏幕宽度
|
||||
*/
|
||||
int getScreenWidth() const { return screenWidth_; }
|
||||
|
||||
/**
|
||||
* @brief 获取屏幕高度
|
||||
* @return 屏幕高度
|
||||
*/
|
||||
int getScreenHeight() const { return screenHeight_; }
|
||||
|
||||
/**
|
||||
* @brief 获取屏幕尺寸
|
||||
* @return 屏幕尺寸
|
||||
*/
|
||||
Size getScreenSize() const {
|
||||
return Size(static_cast<float>(screenWidth_),
|
||||
static_cast<float>(screenHeight_));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取X方向缩放比例
|
||||
* @return X方向缩放比例
|
||||
*/
|
||||
float getScaleX() const { return result_.scaleX; }
|
||||
|
||||
/**
|
||||
* @brief 获取Y方向缩放比例
|
||||
* @return Y方向缩放比例
|
||||
*/
|
||||
float getScaleY() const { return result_.scaleY; }
|
||||
|
||||
/**
|
||||
* @brief 获取统一缩放比例
|
||||
* @return 统一缩放比例
|
||||
*/
|
||||
float getUniformScale() const { return result_.uniformScale; }
|
||||
|
||||
/**
|
||||
* @brief 获取视口偏移
|
||||
* @return 视口偏移
|
||||
*/
|
||||
Vec2 getOffset() const { return result_.offset; }
|
||||
|
||||
/**
|
||||
* @brief 获取视口矩形
|
||||
* @return 视口矩形
|
||||
*/
|
||||
Rect getViewport() const { return result_.viewport; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否有黑边
|
||||
* @return 如果有黑边返回 true
|
||||
*/
|
||||
bool hasLetterbox() const { return result_.hasLetterbox; }
|
||||
|
||||
/**
|
||||
* @brief 获取黑边信息
|
||||
* @return 黑边信息结构体
|
||||
*/
|
||||
const ViewportResult::Letterbox &getLetterbox() const {
|
||||
return result_.letterbox;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 计算宽高比适配模式
|
||||
*/
|
||||
void calculateAspectRatio();
|
||||
|
||||
/**
|
||||
* @brief 计算拉伸适配模式
|
||||
*/
|
||||
void calculateStretch();
|
||||
|
||||
/**
|
||||
* @brief 计算居中适配模式
|
||||
*/
|
||||
void calculateCenter();
|
||||
|
||||
/**
|
||||
* @brief 计算自定义适配模式
|
||||
*/
|
||||
void calculateCustom();
|
||||
|
||||
/**
|
||||
* @brief 计算黑边区域
|
||||
*/
|
||||
void calculateLetterbox();
|
||||
|
||||
/**
|
||||
* @brief 根据黑边位置调整偏移
|
||||
* @param extraWidth 额外宽度
|
||||
* @param extraHeight 额外高度
|
||||
*/
|
||||
void applyLetterboxPosition(float extraWidth, float extraHeight);
|
||||
|
||||
ViewportConfig config_;
|
||||
ViewportResult result_;
|
||||
int screenWidth_ = 0;
|
||||
int screenHeight_ = 0;
|
||||
|
||||
mutable glm::mat4 viewportMatrix_;
|
||||
mutable glm::mat4 inverseViewportMatrix_;
|
||||
mutable bool matrixDirty_ = true;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -3,12 +3,13 @@
|
|||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/resources/pipeline.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Window;
|
||||
class IWindow;
|
||||
class Texture;
|
||||
class FontAtlas;
|
||||
class Shader;
|
||||
|
|
@ -24,15 +25,7 @@ enum class BackendType {
|
|||
// D3D12
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 混合模式
|
||||
// ============================================================================
|
||||
enum class BlendMode {
|
||||
None, // 不混合
|
||||
Alpha, // 标准 Alpha 混合
|
||||
Additive, // 加法混合
|
||||
Multiply // 乘法混合
|
||||
};
|
||||
// BlendMode 定义在 pipeline.h 中
|
||||
|
||||
// ============================================================================
|
||||
// 渲染后端抽象接口
|
||||
|
|
@ -44,7 +37,7 @@ public:
|
|||
// ------------------------------------------------------------------------
|
||||
// 生命周期
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool init(Window *window) = 0;
|
||||
virtual bool init(IWindow* window) = 0;
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -78,6 +71,10 @@ public:
|
|||
// ------------------------------------------------------------------------
|
||||
// 精灵批渲染
|
||||
// ------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief 开始手动精灵批处理(高级用法)
|
||||
* @note 一般情况下不需要调用,drawSprite/drawText 会自动管理批处理
|
||||
*/
|
||||
virtual void beginSpriteBatch() = 0;
|
||||
virtual void drawSprite(const Texture &texture, const Rect &destRect,
|
||||
const Rect &srcRect, const Color &tint,
|
||||
|
|
@ -86,6 +83,12 @@ public:
|
|||
const Color &tint) = 0;
|
||||
virtual void endSpriteBatch() = 0;
|
||||
|
||||
/**
|
||||
* @brief 立即提交当前批处理
|
||||
* @note 手动控制批处理提交时机,一般情况下不需要调用
|
||||
*/
|
||||
virtual void flush() = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 形状渲染
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <cstdint>
|
||||
#include <variant>
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <functional>
|
||||
#include <typeindex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 渲染模块配置结构
|
||||
*/
|
||||
struct RenderCfg {
|
||||
BackendType backend;
|
||||
int targetFPS;
|
||||
bool vsync;
|
||||
int multisamples;
|
||||
int priority;
|
||||
|
||||
RenderCfg()
|
||||
: backend(BackendType::OpenGL)
|
||||
, targetFPS(60)
|
||||
, vsync(true)
|
||||
, multisamples(0)
|
||||
, priority(10)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染模块
|
||||
* 管理渲染后端
|
||||
*/
|
||||
class RenderModule : public Module {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数(Lambda 配置)
|
||||
* @param configFn 配置函数
|
||||
*/
|
||||
explicit RenderModule(std::function<void(RenderCfg&)> configFn);
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~RenderModule() override;
|
||||
|
||||
bool init() override;
|
||||
void shutdown() override;
|
||||
bool ok() const override { return initialized_; }
|
||||
const char* name() const override { return "render"; }
|
||||
int priority() const override { return cfg_.priority; }
|
||||
|
||||
/**
|
||||
* @brief 获取依赖
|
||||
* @return 依赖模块类型列表
|
||||
*/
|
||||
std::vector<std::type_index> deps() const override {
|
||||
return {std::type_index(typeid(WindowModule))};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取渲染器
|
||||
* @return 渲染后端指针
|
||||
*/
|
||||
RenderBackend* renderer() const { return renderer_.get(); }
|
||||
|
||||
private:
|
||||
RenderCfg cfg_;
|
||||
UniquePtr<RenderBackend> renderer_;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <mutex>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -16,7 +16,6 @@ struct RenderTargetConfig {
|
|||
int height = 600; // 高度
|
||||
PixelFormat colorFormat = PixelFormat::RGBA8; // 颜色格式
|
||||
bool hasDepth = true; // 是否包含深度缓冲
|
||||
bool hasDepthBuffer = true; // 兼容旧API的别名 (同hasDepth)
|
||||
bool hasStencil = false; // 是否包含模板缓冲
|
||||
int samples = 1; // 多重采样数 (1 = 无MSAA)
|
||||
bool autoResize = true; // 是否自动调整大小
|
||||
|
|
@ -47,11 +46,6 @@ public:
|
|||
*/
|
||||
bool create(const RenderTargetConfig &config);
|
||||
|
||||
/**
|
||||
* @brief 初始化渲染目标(create的别名,兼容旧API)
|
||||
*/
|
||||
bool init(const RenderTargetConfig &config) { return create(config); }
|
||||
|
||||
/**
|
||||
* @brief 从现有纹理创建渲染目标
|
||||
*/
|
||||
|
|
@ -62,11 +56,6 @@ public:
|
|||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 关闭渲染目标(destroy的别名,兼容旧API)
|
||||
*/
|
||||
void shutdown() { destroy(); }
|
||||
|
||||
/**
|
||||
* @brief 检查是否有效
|
||||
*/
|
||||
|
|
@ -144,14 +133,6 @@ public:
|
|||
*/
|
||||
void copyTo(RenderTarget &target);
|
||||
|
||||
/**
|
||||
* @brief 复制到另一个渲染目标(blitTo的别名,兼容旧API)
|
||||
* @param target 目标渲染目标
|
||||
* @param color 是否复制颜色缓冲
|
||||
* @param depth 是否复制深度缓冲
|
||||
*/
|
||||
void blitTo(RenderTarget &target, bool color = true, bool depth = false);
|
||||
|
||||
/**
|
||||
* @brief 复制到屏幕
|
||||
*/
|
||||
|
|
@ -233,7 +214,7 @@ private:
|
|||
// ============================================================================
|
||||
class RenderTargetStack {
|
||||
public:
|
||||
static RenderTargetStack &getInstance();
|
||||
static RenderTargetStack &get();
|
||||
|
||||
/**
|
||||
* @brief 压入渲染目标
|
||||
|
|
@ -271,12 +252,12 @@ private:
|
|||
// ============================================================================
|
||||
// 渲染目标管理器 - 全局渲染目标管理
|
||||
// ============================================================================
|
||||
class RenderTargetManager {
|
||||
class RenderTargetMgr {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
*/
|
||||
static RenderTargetManager &getInstance();
|
||||
static RenderTargetMgr& get();
|
||||
|
||||
/**
|
||||
* @brief 初始化渲染目标管理器
|
||||
|
|
@ -313,10 +294,10 @@ public:
|
|||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
private:
|
||||
RenderTargetManager() = default;
|
||||
~RenderTargetManager() = default;
|
||||
RenderTargetManager(const RenderTargetManager &) = delete;
|
||||
RenderTargetManager &operator=(const RenderTargetManager &) = delete;
|
||||
RenderTargetMgr() = default;
|
||||
~RenderTargetMgr() = default;
|
||||
RenderTargetMgr(const RenderTargetMgr &) = delete;
|
||||
RenderTargetMgr &operator=(const RenderTargetMgr &) = delete;
|
||||
|
||||
Ptr<RenderTarget> defaultRenderTarget_;
|
||||
std::vector<Ptr<RenderTarget>> renderTargets_;
|
||||
|
|
@ -326,8 +307,7 @@ private:
|
|||
// ============================================================================
|
||||
// 便捷宏
|
||||
// ============================================================================
|
||||
#define E2D_RENDER_TARGET_STACK() ::extra2d::RenderTargetStack::getInstance()
|
||||
#define E2D_RENDER_TARGET_MANAGER() \
|
||||
::extra2d::RenderTargetManager::getInstance()
|
||||
#define E2D_RENDER_TARGET_STACK() ::extra2d::RenderTargetStack::get()
|
||||
#define E2D_RENDER_TARGET_MGR() ::extra2d::RenderTargetMgr::get()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -13,7 +13,7 @@ namespace extra2d {
|
|||
class GPUContext {
|
||||
public:
|
||||
/// 获取单例实例
|
||||
static GPUContext& getInstance();
|
||||
static GPUContext& get();
|
||||
|
||||
/// 标记 GPU 上下文为有效(在初始化完成后调用)
|
||||
void markValid();
|
||||
|
|
@ -9,9 +9,9 @@ namespace extra2d {
|
|||
// ============================================================================
|
||||
// VRAM 管理器 - 跟踪显存使用情况
|
||||
// ============================================================================
|
||||
class VRAMManager {
|
||||
class VRAMMgr {
|
||||
public:
|
||||
static VRAMManager& getInstance();
|
||||
static VRAMMgr& get();
|
||||
|
||||
// 纹理显存跟踪
|
||||
void allocTexture(size_t size);
|
||||
|
|
@ -39,10 +39,10 @@ public:
|
|||
void reset();
|
||||
|
||||
private:
|
||||
VRAMManager();
|
||||
~VRAMManager() = default;
|
||||
VRAMManager(const VRAMManager&) = delete;
|
||||
VRAMManager& operator=(const VRAMManager&) = delete;
|
||||
VRAMMgr();
|
||||
~VRAMMgr() = default;
|
||||
VRAMMgr(const VRAMMgr&) = delete;
|
||||
VRAMMgr& operator=(const VRAMMgr&) = delete;
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/graphics/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <memory>
|
||||
#include <stb/stb_rect_pack.h>
|
||||
#include <stb/stb_truetype.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 字体图集实现 - 使用 stb_rect_pack 进行矩形打包
|
||||
// ============================================================================
|
||||
class GLFontAtlas : public FontAtlas {
|
||||
public:
|
||||
GLFontAtlas(const std::string &filepath, int fontSize, bool useSDF = false);
|
||||
~GLFontAtlas();
|
||||
|
||||
// FontAtlas 接口实现
|
||||
const Glyph *getGlyph(char32_t codepoint) const override;
|
||||
Texture *getTexture() const override { return texture_.get(); }
|
||||
int getFontSize() const override { return fontSize_; }
|
||||
float getAscent() const override { return ascent_; }
|
||||
float getDescent() const override { return descent_; }
|
||||
float getLineGap() const override { return lineGap_; }
|
||||
float getLineHeight() const override { return ascent_ - descent_ + lineGap_; }
|
||||
Vec2 measureText(const std::string &text) override;
|
||||
bool isSDF() const override { return useSDF_; }
|
||||
|
||||
private:
|
||||
// 图集配置 - 增大尺寸以支持更多字符
|
||||
static constexpr int ATLAS_WIDTH = 1024;
|
||||
static constexpr int ATLAS_HEIGHT = 1024;
|
||||
static constexpr int PADDING = 2; // 字形之间的间距
|
||||
|
||||
int fontSize_;
|
||||
bool useSDF_;
|
||||
mutable std::unique_ptr<GLTexture> texture_;
|
||||
mutable std::unordered_map<char32_t, Glyph> glyphs_;
|
||||
|
||||
// stb_rect_pack 上下文
|
||||
mutable stbrp_context packContext_;
|
||||
mutable std::vector<stbrp_node> packNodes_;
|
||||
mutable int currentY_;
|
||||
|
||||
std::vector<unsigned char> fontData_;
|
||||
stbtt_fontinfo fontInfo_;
|
||||
float scale_;
|
||||
float ascent_;
|
||||
float descent_;
|
||||
float lineGap_;
|
||||
|
||||
// 预分配字形位图缓冲区,避免每次动态分配
|
||||
mutable std::vector<uint8_t> glyphBitmapCache_;
|
||||
mutable std::vector<uint8_t> glyphRgbaCache_;
|
||||
|
||||
void createAtlas();
|
||||
void cacheGlyph(char32_t codepoint) const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL Shader 程序
|
||||
// ============================================================================
|
||||
class GLShader {
|
||||
public:
|
||||
GLShader();
|
||||
~GLShader();
|
||||
|
||||
// 从源码编译
|
||||
bool compileFromSource(const char* vertexSource, const char* fragmentSource);
|
||||
|
||||
// 从文件加载并编译
|
||||
bool compileFromFile(const std::string& vertexPath, const std::string& fragmentPath);
|
||||
|
||||
// 使用/激活
|
||||
void bind() const;
|
||||
void unbind() const;
|
||||
|
||||
// Uniform 设置
|
||||
void setBool(const std::string& name, bool value);
|
||||
void setInt(const std::string& name, int value);
|
||||
void setFloat(const std::string& name, float value);
|
||||
void setVec2(const std::string& name, const glm::vec2& value);
|
||||
void setVec3(const std::string& name, const glm::vec3& value);
|
||||
void setVec4(const std::string& name, const glm::vec4& value);
|
||||
void setMat4(const std::string& name, const glm::mat4& value);
|
||||
|
||||
// 获取程序 ID
|
||||
GLuint getProgramID() const { return programID_; }
|
||||
|
||||
// 检查是否有效
|
||||
bool isValid() const { return programID_ != 0; }
|
||||
|
||||
private:
|
||||
GLuint programID_;
|
||||
std::unordered_map<std::string, GLint> uniformCache_;
|
||||
|
||||
GLuint compileShader(GLenum type, const char* source);
|
||||
GLint getUniformLocation(const std::string& name);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/opengl/gl_shader.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 精灵批渲染器 - 优化版本
|
||||
// ============================================================================
|
||||
class GLSpriteBatch {
|
||||
public:
|
||||
static constexpr size_t MAX_SPRITES = 10000;
|
||||
static constexpr size_t VERTICES_PER_SPRITE = 4;
|
||||
static constexpr size_t INDICES_PER_SPRITE = 6;
|
||||
static constexpr size_t MAX_VERTICES = MAX_SPRITES * VERTICES_PER_SPRITE;
|
||||
static constexpr size_t MAX_INDICES = MAX_SPRITES * INDICES_PER_SPRITE;
|
||||
|
||||
struct Vertex {
|
||||
glm::vec2 position;
|
||||
glm::vec2 texCoord;
|
||||
glm::vec4 color;
|
||||
};
|
||||
|
||||
struct SpriteData {
|
||||
glm::vec2 position;
|
||||
glm::vec2 size;
|
||||
glm::vec2 texCoordMin;
|
||||
glm::vec2 texCoordMax;
|
||||
glm::vec4 color;
|
||||
float rotation;
|
||||
glm::vec2 anchor;
|
||||
bool isSDF = false;
|
||||
};
|
||||
|
||||
GLSpriteBatch();
|
||||
~GLSpriteBatch();
|
||||
|
||||
bool init();
|
||||
void shutdown();
|
||||
|
||||
void begin(const glm::mat4 &viewProjection);
|
||||
void draw(const Texture &texture, const SpriteData &data);
|
||||
void end();
|
||||
|
||||
// 批量绘制接口 - 用于自动批处理
|
||||
void drawBatch(const Texture& texture, const std::vector<SpriteData>& sprites);
|
||||
|
||||
// 立即绘制(不缓存)
|
||||
void drawImmediate(const Texture& texture, const SpriteData& data);
|
||||
|
||||
// 统计
|
||||
uint32_t getDrawCallCount() const { return drawCallCount_; }
|
||||
uint32_t getSpriteCount() const { return spriteCount_; }
|
||||
uint32_t getBatchCount() const { return batchCount_; }
|
||||
|
||||
// 检查是否需要刷新
|
||||
bool needsFlush(const Texture& texture, bool isSDF) const;
|
||||
|
||||
private:
|
||||
GLuint vao_;
|
||||
GLuint vbo_;
|
||||
GLuint ibo_;
|
||||
GLShader shader_;
|
||||
|
||||
// 使用固定大小数组减少内存分配
|
||||
std::array<Vertex, MAX_VERTICES> vertexBuffer_;
|
||||
size_t vertexCount_;
|
||||
|
||||
const Texture *currentTexture_;
|
||||
bool currentIsSDF_;
|
||||
glm::mat4 viewProjection_;
|
||||
|
||||
// 缓存上一帧的 viewProjection,避免重复设置
|
||||
glm::mat4 cachedViewProjection_;
|
||||
bool viewProjectionDirty_ = true;
|
||||
|
||||
uint32_t drawCallCount_;
|
||||
uint32_t spriteCount_;
|
||||
uint32_t batchCount_;
|
||||
|
||||
void flush();
|
||||
void setupShader();
|
||||
|
||||
// 添加顶点到缓冲区
|
||||
void addVertices(const SpriteData& data);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 缓冲区类型枚举
|
||||
// ============================================================================
|
||||
enum class BufferType {
|
||||
Vertex, // 顶点缓冲
|
||||
Index, // 索引缓冲
|
||||
Uniform // 统一缓冲
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 缓冲区使用模式枚举
|
||||
// ============================================================================
|
||||
enum class BufferUsage {
|
||||
Static, // 静态数据,很少更新
|
||||
Dynamic, // 动态数据,频繁更新
|
||||
Stream // 流式数据,每帧更新
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 缓冲区描述结构
|
||||
// ============================================================================
|
||||
struct BufferDesc {
|
||||
BufferType type = BufferType::Vertex;
|
||||
BufferUsage usage = BufferUsage::Static;
|
||||
size_t size = 0; // 缓冲区大小(字节)
|
||||
const void* initialData = nullptr; // 初始数据
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 缓冲区抽象接口 - 渲染后端无关
|
||||
// ============================================================================
|
||||
class Buffer {
|
||||
public:
|
||||
virtual ~Buffer() = default;
|
||||
|
||||
/**
|
||||
* @brief 绑定缓冲区
|
||||
*/
|
||||
virtual void bind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 解绑缓冲区
|
||||
*/
|
||||
virtual void unbind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置缓冲区数据(完全替换)
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小(字节)
|
||||
*/
|
||||
virtual void setData(const void* data, size_t size) = 0;
|
||||
|
||||
/**
|
||||
* @brief 更新缓冲区部分数据
|
||||
* @param data 数据指针
|
||||
* @param offset 偏移量(字节)
|
||||
* @param size 数据大小(字节)
|
||||
*/
|
||||
virtual void updateData(const void* data, size_t offset, size_t size) = 0;
|
||||
|
||||
/**
|
||||
* @brief 映射缓冲区到内存(用于直接写入)
|
||||
* @return 映射后的内存指针,失败返回 nullptr
|
||||
*/
|
||||
virtual void* map() = 0;
|
||||
|
||||
/**
|
||||
* @brief 解除缓冲区映射
|
||||
*/
|
||||
virtual void unmap() = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取缓冲区大小
|
||||
* @return 缓冲区大小(字节)
|
||||
*/
|
||||
virtual size_t getSize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取缓冲区类型
|
||||
* @return 缓冲区类型
|
||||
*/
|
||||
virtual BufferType getType() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取缓冲区使用模式
|
||||
* @return 使用模式
|
||||
*/
|
||||
virtual BufferUsage getUsage() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查缓冲区是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄(后端特定)
|
||||
* @return 原生句柄值
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 字形信息结构
|
||||
// ============================================================================
|
||||
struct Glyph {
|
||||
float width = 0; // 字形宽度
|
||||
float height = 0; // 字形高度
|
||||
float bearingX = 0; // 水平偏移
|
||||
float bearingY = 0; // 垂直偏移(从基线到字形顶部)
|
||||
float advance = 0; // 水平步进
|
||||
float u0 = 0, v0 = 0; // 纹理坐标左下角
|
||||
float u1 = 0, v1 = 0; // 纹理坐标右上角
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 字体图集描述结构
|
||||
// ============================================================================
|
||||
struct FontAtlasDesc {
|
||||
std::string filepath; // 字体文件路径
|
||||
int fontSize = 16; // 字体大小
|
||||
bool useSDF = false; // 是否使用SDF渲染
|
||||
int atlasSize = 512; // 图集大小
|
||||
int padding = 2; // 字形间距
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 字体图集抽象接口 - 渲染后端无关
|
||||
// ============================================================================
|
||||
class FontAtlas {
|
||||
public:
|
||||
virtual ~FontAtlas() = default;
|
||||
|
||||
/**
|
||||
* @brief 初始化字体图集
|
||||
* @param desc 字体图集描述
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
virtual bool init(const FontAtlasDesc& desc) = 0;
|
||||
|
||||
/**
|
||||
* @brief 关闭字体图集,释放资源
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取字形信息
|
||||
* @param codepoint Unicode 码点
|
||||
* @return 字形信息指针,未找到返回 nullptr
|
||||
*/
|
||||
virtual const Glyph* getGlyph(char32_t codepoint) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取纹理
|
||||
* @return 纹理对象
|
||||
*/
|
||||
virtual Ptr<Texture> getTexture() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取字体大小
|
||||
* @return 字体大小
|
||||
*/
|
||||
virtual int getFontSize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取行高
|
||||
* @return 行高
|
||||
*/
|
||||
virtual float getLineHeight() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取基线到顶部的距离
|
||||
* @return 上升高度
|
||||
*/
|
||||
virtual float getAscent() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取基线到底部的距离
|
||||
* @return 下降高度
|
||||
*/
|
||||
virtual float getDescent() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 计算文本宽度
|
||||
* @param text 文本内容
|
||||
* @return 文本宽度
|
||||
*/
|
||||
virtual float measureText(const std::string& text) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 计算文本尺寸
|
||||
* @param text 文本内容
|
||||
* @return 文本尺寸
|
||||
*/
|
||||
virtual Size measureTextSize(const std::string& text) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 是否使用SDF渲染
|
||||
* @return 使用SDF返回 true
|
||||
*/
|
||||
virtual bool isSDF() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查字体图集是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 预加载字符到图集
|
||||
* @param text 需要预加载的文本
|
||||
* @return 成功加载的字符数
|
||||
*/
|
||||
virtual int preloadGlyphs(const std::string& text) = 0;
|
||||
|
||||
/**
|
||||
* @brief 清空已加载的字形缓存
|
||||
*/
|
||||
virtual void clearCache() = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 帧缓冲描述结构
|
||||
// ============================================================================
|
||||
struct FramebufferDesc {
|
||||
int width = 0; // 帧缓冲宽度
|
||||
int height = 0; // 帧缓冲高度
|
||||
int colorAttachments = 1; // 颜色附件数量
|
||||
bool hasDepth = false; // 是否有深度附件
|
||||
bool hasStencil = false; // 是否有模板附件
|
||||
bool multisample = false; // 是否多重采样
|
||||
int samples = 4; // 采样数(多重采样时有效)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 帧缓冲抽象接口 - 渲染后端无关
|
||||
// ============================================================================
|
||||
class Framebuffer {
|
||||
public:
|
||||
virtual ~Framebuffer() = default;
|
||||
|
||||
/**
|
||||
* @brief 绑定帧缓冲(作为渲染目标)
|
||||
*/
|
||||
virtual void bind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 解绑帧缓冲(恢复到默认帧缓冲)
|
||||
*/
|
||||
virtual void unbind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 附加颜色纹理
|
||||
* @param texture 纹理对象
|
||||
* @param attachment 附件索引(0-7)
|
||||
*/
|
||||
virtual void attachColorTexture(Ptr<Texture> texture, int attachment = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief 附加深度纹理
|
||||
* @param texture 纹理对象
|
||||
*/
|
||||
virtual void attachDepthTexture(Ptr<Texture> texture) = 0;
|
||||
|
||||
/**
|
||||
* @brief 附加深度模板纹理
|
||||
* @param texture 纹理对象
|
||||
*/
|
||||
virtual void attachDepthStencilTexture(Ptr<Texture> texture) = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查帧缓冲是否完整
|
||||
* @return 完整返回 true
|
||||
*/
|
||||
virtual bool isComplete() = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取颜色附件纹理
|
||||
* @param attachment 附件索引
|
||||
* @return 纹理对象
|
||||
*/
|
||||
virtual Ptr<Texture> getColorTexture(int attachment = 0) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取深度附件纹理
|
||||
* @return 纹理对象
|
||||
*/
|
||||
virtual Ptr<Texture> getDepthTexture() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取帧缓冲宽度
|
||||
* @return 宽度
|
||||
*/
|
||||
virtual int getWidth() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取帧缓冲高度
|
||||
* @return 高度
|
||||
*/
|
||||
virtual int getHeight() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取尺寸
|
||||
* @return 尺寸
|
||||
*/
|
||||
virtual Size getSize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查帧缓冲是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄(后端特定)
|
||||
* @return 原生句柄值
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 清除帧缓冲
|
||||
* @param color 清除颜色
|
||||
* @param clearColor 是否清除颜色缓冲
|
||||
* @param clearDepth 是否清除深度缓冲
|
||||
* @param clearStencil 是否清除模板缓冲
|
||||
*/
|
||||
virtual void clear(const Color& color, bool clearColor = true,
|
||||
bool clearDepth = true, bool clearStencil = false) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置视口
|
||||
* @param x 视口左下角X坐标
|
||||
* @param y 视口左下角Y坐标
|
||||
* @param width 视口宽度
|
||||
* @param height 视口高度
|
||||
*/
|
||||
virtual void setViewport(int x, int y, int width, int height) = 0;
|
||||
|
||||
/**
|
||||
* @brief 读取像素数据
|
||||
* @param x 起始X坐标
|
||||
* @param y 起始Y坐标
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param outData 输出数据缓冲区
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
virtual bool readPixels(int x, int y, int width, int height,
|
||||
std::vector<uint8_t>& outData) = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 混合模式枚举
|
||||
// ============================================================================
|
||||
enum class BlendMode {
|
||||
None, // 不混合
|
||||
Alpha, // 标准 Alpha 混合
|
||||
Additive, // 加法混合
|
||||
Multiply // 乘法混合
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 深度测试函数枚举
|
||||
// ============================================================================
|
||||
enum class DepthFunc {
|
||||
Never, // 永不通过
|
||||
Less, // 小于
|
||||
Equal, // 等于
|
||||
LessEqual, // 小于等于
|
||||
Greater, // 大于
|
||||
NotEqual, // 不等于
|
||||
GreaterEqual,// 大于等于
|
||||
Always // 总是通过
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 裁剪模式枚举
|
||||
// ============================================================================
|
||||
enum class CullMode {
|
||||
None, // 不裁剪
|
||||
Front, // 裁剪正面
|
||||
Back, // 裁剪背面
|
||||
Both // 裁剪双面
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 顶点属性格式枚举
|
||||
// ============================================================================
|
||||
enum class VertexFormat {
|
||||
Float1, // 1个float
|
||||
Float2, // 2个float
|
||||
Float3, // 3个float
|
||||
Float4, // 4个float
|
||||
Byte4, // 4个byte
|
||||
UByte4, // 4个ubyte
|
||||
Short2, // 2个short
|
||||
Short4 // 4个short
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 顶点属性描述
|
||||
// ============================================================================
|
||||
struct VertexAttribute {
|
||||
uint32_t location = 0; // 属性位置
|
||||
VertexFormat format = VertexFormat::Float3; // 数据格式
|
||||
uint32_t offset = 0; // 在顶点结构中的偏移
|
||||
uint32_t stride = 0; // 顶点结构大小
|
||||
bool normalized = false; // 是否归一化
|
||||
|
||||
VertexAttribute() = default;
|
||||
VertexAttribute(uint32_t loc, VertexFormat fmt, uint32_t off, uint32_t str, bool norm = false)
|
||||
: location(loc), format(fmt), offset(off), stride(str), normalized(norm) {}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 管线描述结构
|
||||
// ============================================================================
|
||||
struct PipelineDesc {
|
||||
// 混合状态
|
||||
BlendMode blendMode = BlendMode::Alpha;
|
||||
bool blendEnabled = true;
|
||||
|
||||
// 深度状态
|
||||
bool depthTest = false;
|
||||
bool depthWrite = false;
|
||||
DepthFunc depthFunc = DepthFunc::Less;
|
||||
|
||||
// 裁剪状态
|
||||
CullMode cullMode = CullMode::None;
|
||||
|
||||
// 顶点布局
|
||||
std::vector<VertexAttribute> vertexAttributes;
|
||||
|
||||
// 着色器(由后端特定实现设置)
|
||||
void* vertexShader = nullptr;
|
||||
void* fragmentShader = nullptr;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 渲染管线抽象接口 - 渲染后端无关
|
||||
// ============================================================================
|
||||
class Pipeline {
|
||||
public:
|
||||
virtual ~Pipeline() = default;
|
||||
|
||||
/**
|
||||
* @brief 绑定管线
|
||||
*/
|
||||
virtual void bind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 解绑管线
|
||||
*/
|
||||
virtual void unbind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置混合模式
|
||||
* @param mode 混合模式
|
||||
*/
|
||||
virtual void setBlendMode(BlendMode mode) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取当前混合模式
|
||||
* @return 混合模式
|
||||
*/
|
||||
virtual BlendMode getBlendMode() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置深度测试
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
virtual void setDepthTest(bool enabled) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置深度写入
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
virtual void setDepthWrite(bool enabled) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置深度测试函数
|
||||
* @param func 深度测试函数
|
||||
*/
|
||||
virtual void setDepthFunc(DepthFunc func) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置裁剪模式
|
||||
* @param mode 裁剪模式
|
||||
*/
|
||||
virtual void setCullMode(CullMode mode) = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查管线是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄(后端特定)
|
||||
* @return 原生句柄值
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 着色器类型枚举
|
||||
// ============================================================================
|
||||
enum class ShaderType {
|
||||
Vertex, // 顶点着色器
|
||||
Fragment, // 片段着色器
|
||||
Geometry, // 几何着色器
|
||||
Compute // 计算着色器
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 着色器描述结构
|
||||
// ============================================================================
|
||||
struct ShaderDesc {
|
||||
std::string name; // 着色器名称
|
||||
std::string vertexSource; // 顶点着色器源码
|
||||
std::string fragmentSource; // 片段着色器源码
|
||||
std::string geometrySource; // 几何着色器源码(可选)
|
||||
std::vector<uint8_t> binaryData; // 预编译二进制数据(可选)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 着色器抽象接口 - 渲染后端无关
|
||||
// ============================================================================
|
||||
class Shader {
|
||||
public:
|
||||
virtual ~Shader() = default;
|
||||
|
||||
/**
|
||||
* @brief 绑定着色器程序
|
||||
*/
|
||||
virtual void bind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 解绑着色器程序
|
||||
*/
|
||||
virtual void unbind() = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置布尔类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param value 布尔值
|
||||
*/
|
||||
virtual void setBool(const std::string& name, bool value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置整数类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param value 整数值
|
||||
*/
|
||||
virtual void setInt(const std::string& name, int value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置浮点类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param value 浮点值
|
||||
*/
|
||||
virtual void setFloat(const std::string& name, float value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置二维向量类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param value 二维向量值
|
||||
*/
|
||||
virtual void setVec2(const std::string& name, const glm::vec2& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置三维向量类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param value 三维向量值
|
||||
*/
|
||||
virtual void setVec3(const std::string& name, const glm::vec3& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置四维向量类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param value 四维向量值
|
||||
*/
|
||||
virtual void setVec4(const std::string& name, const glm::vec4& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置 4x4 矩阵类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param value 4x4 矩阵值
|
||||
*/
|
||||
virtual void setMat4(const std::string& name, const glm::mat4& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置颜色类型 uniform 变量
|
||||
* @param name uniform 变量名
|
||||
* @param color 颜色值
|
||||
*/
|
||||
virtual void setColor(const std::string& name, const Color& color) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置纹理采样器
|
||||
* @param name uniform 变量名
|
||||
* @param slot 纹理槽位
|
||||
*/
|
||||
virtual void setTexture(const std::string& name, int slot) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取着色器名称
|
||||
* @return 着色器名称
|
||||
*/
|
||||
virtual const std::string& getName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查着色器是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄(后端特定)
|
||||
* @return 原生句柄值
|
||||
*/
|
||||
virtual uintptr_t getNativeHandle() const = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// Shader缓存条目
|
||||
// ============================================================================
|
||||
struct ShaderCacheEntry {
|
||||
std::string name;
|
||||
std::string sourceHash;
|
||||
uint64_t compileTime = 0;
|
||||
std::vector<uint8_t> binary;
|
||||
std::vector<std::string> dependencies;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Shader缓存管理器
|
||||
// ============================================================================
|
||||
class ShaderCache {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 缓存管理器实例引用
|
||||
*/
|
||||
static ShaderCache& getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化缓存系统
|
||||
* @param cacheDir 缓存目录路径
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool init(const std::string& cacheDir);
|
||||
|
||||
/**
|
||||
* @brief 关闭缓存系统
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查缓存是否有效
|
||||
* @param name Shader名称
|
||||
* @param sourceHash 源码哈希值
|
||||
* @return 缓存有效返回true,否则返回false
|
||||
*/
|
||||
bool hasValidCache(const std::string& name, const std::string& sourceHash);
|
||||
|
||||
/**
|
||||
* @brief 加载缓存的二进制数据
|
||||
* @param name Shader名称
|
||||
* @return 缓存条目指针,不存在返回nullptr
|
||||
*/
|
||||
Ptr<ShaderCacheEntry> loadCache(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 保存编译结果到缓存
|
||||
* @param entry 缓存条目
|
||||
* @return 保存成功返回true,失败返回false
|
||||
*/
|
||||
bool saveCache(const ShaderCacheEntry& entry);
|
||||
|
||||
/**
|
||||
* @brief 使缓存失效
|
||||
* @param name Shader名称
|
||||
*/
|
||||
void invalidate(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 清除所有缓存
|
||||
*/
|
||||
void clearAll();
|
||||
|
||||
/**
|
||||
* @brief 计算源码哈希值
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 哈希值字符串
|
||||
*/
|
||||
static std::string computeHash(const std::string& vertSource,
|
||||
const std::string& fragSource);
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
* @return 已初始化返回true,否则返回false
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
private:
|
||||
ShaderCache() = default;
|
||||
~ShaderCache() = default;
|
||||
ShaderCache(const ShaderCache&) = delete;
|
||||
ShaderCache& operator=(const ShaderCache&) = delete;
|
||||
|
||||
std::string cacheDir_;
|
||||
std::unordered_map<std::string, ShaderCacheEntry> cacheMap_;
|
||||
bool initialized_ = false;
|
||||
|
||||
/**
|
||||
* @brief 加载缓存索引
|
||||
* @return 加载成功返回true,失败返回false
|
||||
*/
|
||||
bool loadCacheIndex();
|
||||
|
||||
/**
|
||||
* @brief 保存缓存索引
|
||||
* @return 保存成功返回true,失败返回false
|
||||
*/
|
||||
bool saveCacheIndex();
|
||||
|
||||
/**
|
||||
* @brief 获取缓存文件路径
|
||||
* @param name Shader名称
|
||||
* @return 缓存文件完整路径
|
||||
*/
|
||||
std::string getCachePath(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 确保缓存目录存在
|
||||
* @return 目录存在或创建成功返回true,否则返回false
|
||||
*/
|
||||
bool ensureCacheDirectory();
|
||||
};
|
||||
|
||||
// 便捷宏
|
||||
#define E2D_SHADER_CACHE() ::extra2d::ShaderCache::getInstance()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 文件变化事件
|
||||
// ============================================================================
|
||||
struct FileChangeEvent {
|
||||
std::string filepath;
|
||||
|
||||
enum class Type { Created, Modified, Deleted, Renamed } type;
|
||||
|
||||
uint64_t timestamp = 0;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 文件变化回调
|
||||
// ============================================================================
|
||||
using FileChangeCallback = std::function<void(const FileChangeEvent &)>;
|
||||
|
||||
// ============================================================================
|
||||
// Shader热重载管理器
|
||||
// ============================================================================
|
||||
class ShaderHotReloader {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 热重载管理器实例引用
|
||||
*/
|
||||
static ShaderHotReloader &getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化热重载系统
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief 关闭热重载系统
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 注册Shader文件监视
|
||||
* @param shaderName Shader名称
|
||||
* @param filePaths 要监视的文件列表
|
||||
* @param callback 文件变化时的回调
|
||||
*/
|
||||
void watch(const std::string &shaderName,
|
||||
const std::vector<std::string> &filePaths,
|
||||
FileChangeCallback callback);
|
||||
|
||||
/**
|
||||
* @brief 取消监视
|
||||
* @param shaderName Shader名称
|
||||
*/
|
||||
void unwatch(const std::string &shaderName);
|
||||
|
||||
/**
|
||||
* @brief 更新文件监视(在主循环中调用)
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* @brief 启用/禁用热重载
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief 检查是否启用
|
||||
* @return 启用返回true,否则返回false
|
||||
*/
|
||||
bool isEnabled() const { return enabled_; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
* @return 已初始化返回true,否则返回false
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
private:
|
||||
ShaderHotReloader() = default;
|
||||
~ShaderHotReloader() = default;
|
||||
ShaderHotReloader(const ShaderHotReloader &) = delete;
|
||||
ShaderHotReloader &operator=(const ShaderHotReloader &) = delete;
|
||||
|
||||
bool enabled_ = false;
|
||||
bool initialized_ = false;
|
||||
|
||||
struct WatchInfo {
|
||||
std::vector<std::string> filePaths;
|
||||
FileChangeCallback callback;
|
||||
std::unordered_map<std::string, uint64_t> modifiedTimes;
|
||||
};
|
||||
std::unordered_map<std::string, WatchInfo> watchMap_;
|
||||
|
||||
#ifdef _WIN32
|
||||
HANDLE watchHandle_ = nullptr;
|
||||
std::vector<uint8_t> buffer_;
|
||||
std::string watchDir_;
|
||||
bool watching_ = false;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 轮询检查文件变化
|
||||
*/
|
||||
void pollChanges();
|
||||
|
||||
/**
|
||||
* @brief 获取文件修改时间
|
||||
* @param filepath 文件路径
|
||||
* @return 修改时间戳
|
||||
*/
|
||||
static uint64_t getFileModifiedTime(const std::string &filepath);
|
||||
};
|
||||
|
||||
// 便捷宏
|
||||
#define E2D_SHADER_HOT_RELOADER() ::extra2d::ShaderHotReloader::getInstance()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Color;
|
||||
|
||||
// ============================================================================
|
||||
// Shader抽象接口 - 渲染后端无关
|
||||
// ============================================================================
|
||||
class IShader {
|
||||
public:
|
||||
virtual ~IShader() = default;
|
||||
|
||||
/**
|
||||
* @brief 绑定Shader程序
|
||||
*/
|
||||
virtual void bind() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 解绑Shader程序
|
||||
*/
|
||||
virtual void unbind() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置布尔类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 布尔值
|
||||
*/
|
||||
virtual void setBool(const std::string& name, bool value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置整数类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 整数值
|
||||
*/
|
||||
virtual void setInt(const std::string& name, int value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置浮点类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 浮点值
|
||||
*/
|
||||
virtual void setFloat(const std::string& name, float value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置二维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 二维向量值
|
||||
*/
|
||||
virtual void setVec2(const std::string& name, const glm::vec2& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置三维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 三维向量值
|
||||
*/
|
||||
virtual void setVec3(const std::string& name, const glm::vec3& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置四维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 四维向量值
|
||||
*/
|
||||
virtual void setVec4(const std::string& name, const glm::vec4& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置4x4矩阵类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 4x4矩阵值
|
||||
*/
|
||||
virtual void setMat4(const std::string& name, const glm::mat4& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置颜色类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param color 颜色值
|
||||
*/
|
||||
virtual void setColor(const std::string& name, const Color& color) = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查Shader是否有效
|
||||
* @return 有效返回true,否则返回false
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生句柄(如OpenGL程序ID)
|
||||
* @return 原生句柄值
|
||||
*/
|
||||
virtual uint32_t getNativeHandle() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取Shader名称
|
||||
* @return Shader名称
|
||||
*/
|
||||
virtual const std::string& getName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置Shader名称
|
||||
* @param name Shader名称
|
||||
*/
|
||||
virtual void setName(const std::string& name) = 0;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Shader工厂接口 - 用于创建渲染后端特定的Shader实例
|
||||
// ============================================================================
|
||||
class IShaderFactory {
|
||||
public:
|
||||
virtual ~IShaderFactory() = default;
|
||||
|
||||
/**
|
||||
* @brief 从源码创建Shader
|
||||
* @param name Shader名称
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 创建的Shader实例
|
||||
*/
|
||||
virtual Ptr<IShader> createFromSource(
|
||||
const std::string& name,
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource) = 0;
|
||||
|
||||
/**
|
||||
* @brief 从缓存二进制创建Shader
|
||||
* @param name Shader名称
|
||||
* @param binary 编译后的二进制数据
|
||||
* @return 创建的Shader实例
|
||||
*/
|
||||
virtual Ptr<IShader> createFromBinary(
|
||||
const std::string& name,
|
||||
const std::vector<uint8_t>& binary) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取Shader的二进制数据(用于缓存)
|
||||
* @param shader Shader实例
|
||||
* @param outBinary 输出的二进制数据
|
||||
* @return 成功返回true,失败返回false
|
||||
*/
|
||||
virtual bool getShaderBinary(const IShader& shader,
|
||||
std::vector<uint8_t>& outBinary) = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// Shader加载结果
|
||||
// ============================================================================
|
||||
struct ShaderLoadResult {
|
||||
bool success = false;
|
||||
std::string errorMessage;
|
||||
std::string vertSource;
|
||||
std::string fragSource;
|
||||
std::vector<std::string> dependencies;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Shader元数据
|
||||
// ============================================================================
|
||||
struct ShaderMetadata {
|
||||
std::string name;
|
||||
std::string vertPath;
|
||||
std::string fragPath;
|
||||
std::string combinedPath;
|
||||
uint64_t lastModified = 0;
|
||||
std::vector<std::string> defines;
|
||||
std::unordered_map<std::string, std::string> uniforms;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ShaderLoader接口 - 支持多种文件格式加载
|
||||
// ============================================================================
|
||||
class IShaderLoader {
|
||||
public:
|
||||
virtual ~IShaderLoader() = default;
|
||||
|
||||
/**
|
||||
* @brief 从分离文件加载Shader (.vert + .frag)
|
||||
* @param name Shader名称
|
||||
* @param vertPath 顶点着色器文件路径
|
||||
* @param fragPath 片段着色器文件路径
|
||||
* @return 加载结果
|
||||
*/
|
||||
virtual ShaderLoadResult loadFromSeparateFiles(
|
||||
const std::string& name,
|
||||
const std::string& vertPath,
|
||||
const std::string& fragPath) = 0;
|
||||
|
||||
/**
|
||||
* @brief 从组合文件加载Shader (.shader)
|
||||
* @param path 组合Shader文件路径
|
||||
* @return 加载结果
|
||||
*/
|
||||
virtual ShaderLoadResult loadFromCombinedFile(const std::string& path) = 0;
|
||||
|
||||
/**
|
||||
* @brief 从源码字符串加载Shader
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 加载结果
|
||||
*/
|
||||
virtual ShaderLoadResult loadFromSource(
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource) = 0;
|
||||
|
||||
/**
|
||||
* @brief 处理Shader源码中的#include指令
|
||||
* @param source 原始源码
|
||||
* @param baseDir 基础目录
|
||||
* @param outDependencies 输出依赖列表
|
||||
* @return 处理后的源码
|
||||
*/
|
||||
virtual std::string processIncludes(
|
||||
const std::string& source,
|
||||
const std::string& baseDir,
|
||||
std::vector<std::string>& outDependencies) = 0;
|
||||
|
||||
/**
|
||||
* @brief 应用预处理器定义
|
||||
* @param source 原始源码
|
||||
* @param defines 预处理器定义列表
|
||||
* @return 处理后的源码
|
||||
*/
|
||||
virtual std::string applyDefines(
|
||||
const std::string& source,
|
||||
const std::vector<std::string>& defines) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取Shader元数据
|
||||
* @param path Shader文件路径
|
||||
* @return 元数据
|
||||
*/
|
||||
virtual ShaderMetadata getMetadata(const std::string& path) = 0;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 默认ShaderLoader实现
|
||||
// ============================================================================
|
||||
class ShaderLoader : public IShaderLoader {
|
||||
public:
|
||||
ShaderLoader();
|
||||
~ShaderLoader() override = default;
|
||||
|
||||
/**
|
||||
* @brief 从分离文件加载Shader (.vert + .frag)
|
||||
* @param name Shader名称
|
||||
* @param vertPath 顶点着色器文件路径
|
||||
* @param fragPath 片段着色器文件路径
|
||||
* @return 加载结果
|
||||
*/
|
||||
ShaderLoadResult loadFromSeparateFiles(
|
||||
const std::string& name,
|
||||
const std::string& vertPath,
|
||||
const std::string& fragPath) override;
|
||||
|
||||
/**
|
||||
* @brief 从组合文件加载Shader (.shader)
|
||||
* @param path 组合Shader文件路径
|
||||
* @return 加载结果
|
||||
*/
|
||||
ShaderLoadResult loadFromCombinedFile(const std::string& path) override;
|
||||
|
||||
/**
|
||||
* @brief 从源码字符串加载Shader
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 加载结果
|
||||
*/
|
||||
ShaderLoadResult loadFromSource(
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource) override;
|
||||
|
||||
/**
|
||||
* @brief 处理Shader源码中的#include指令
|
||||
* @param source 原始源码
|
||||
* @param baseDir 基础目录
|
||||
* @param outDependencies 输出依赖列表
|
||||
* @return 处理后的源码
|
||||
*/
|
||||
std::string processIncludes(
|
||||
const std::string& source,
|
||||
const std::string& baseDir,
|
||||
std::vector<std::string>& outDependencies) override;
|
||||
|
||||
/**
|
||||
* @brief 应用预处理器定义
|
||||
* @param source 原始源码
|
||||
* @param defines 预处理器定义列表
|
||||
* @return 处理后的源码
|
||||
*/
|
||||
std::string applyDefines(
|
||||
const std::string& source,
|
||||
const std::vector<std::string>& defines) override;
|
||||
|
||||
/**
|
||||
* @brief 获取Shader元数据
|
||||
* @param path Shader文件路径
|
||||
* @return 元数据
|
||||
*/
|
||||
ShaderMetadata getMetadata(const std::string& path) override;
|
||||
|
||||
/**
|
||||
* @brief 添加include搜索路径
|
||||
* @param path 搜索路径
|
||||
*/
|
||||
void addIncludePath(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 读取文件内容
|
||||
* @param filepath 文件路径
|
||||
* @return 文件内容字符串
|
||||
*/
|
||||
static std::string readFile(const std::string& filepath);
|
||||
|
||||
/**
|
||||
* @brief 获取文件修改时间
|
||||
* @param filepath 文件路径
|
||||
* @return 修改时间戳
|
||||
*/
|
||||
static uint64_t getFileModifiedTime(const std::string& filepath);
|
||||
|
||||
/**
|
||||
* @brief 检查文件是否存在
|
||||
* @param filepath 文件路径
|
||||
* @return 存在返回true,否则返回false
|
||||
*/
|
||||
static bool fileExists(const std::string& filepath);
|
||||
|
||||
private:
|
||||
std::vector<std::string> includePaths_;
|
||||
std::unordered_map<std::string, std::string> includeCache_;
|
||||
|
||||
/**
|
||||
* @brief 解析组合Shader文件
|
||||
* @param content 文件内容
|
||||
* @param outVert 输出顶点着色器源码
|
||||
* @param outFrag 输出片段着色器源码
|
||||
* @param outMetadata 输出元数据
|
||||
* @return 解析成功返回true,失败返回false
|
||||
*/
|
||||
bool parseCombinedFile(const std::string& content,
|
||||
std::string& outVert,
|
||||
std::string& outFrag,
|
||||
ShaderMetadata& outMetadata);
|
||||
|
||||
/**
|
||||
* @brief 解析元数据JSON块
|
||||
* @param jsonContent JSON内容
|
||||
* @param outMetadata 输出元数据
|
||||
* @return 解析成功返回true,失败返回false
|
||||
*/
|
||||
bool parseMetadata(const std::string& jsonContent, ShaderMetadata& outMetadata);
|
||||
|
||||
/**
|
||||
* @brief 查找include文件路径
|
||||
* @param includeName include文件名
|
||||
* @param baseDir 基础目录
|
||||
* @return 找到的完整路径,未找到返回空字符串
|
||||
*/
|
||||
std::string findIncludeFile(const std::string& includeName, const std::string& baseDir);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/shader/shader_cache.h>
|
||||
#include <extra2d/graphics/shader/shader_hot_reloader.h>
|
||||
#include <extra2d/graphics/shader/shader_interface.h>
|
||||
#include <extra2d/graphics/shader/shader_loader.h>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// Shader重载回调
|
||||
// ============================================================================
|
||||
using ShaderReloadCallback = std::function<void(Ptr<IShader> newShader)>;
|
||||
|
||||
// ============================================================================
|
||||
// Shader管理器 - 统一入口
|
||||
// ============================================================================
|
||||
class ShaderManager {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return Shader管理器实例引用
|
||||
*/
|
||||
static ShaderManager& getInstance();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 初始化和关闭
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 使用平台默认路径初始化Shader系统
|
||||
* 自动检测平台并使用正确的路径(romfs/sdmc/相对路径)
|
||||
* @param factory 渲染后端Shader工厂
|
||||
* @param appName 应用名称(用于缓存目录)
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool init(Ptr<IShaderFactory> factory, const std::string& appName = "extra2d");
|
||||
|
||||
/**
|
||||
* @brief 初始化Shader系统
|
||||
* @param shaderDir Shader文件目录
|
||||
* @param cacheDir 缓存目录
|
||||
* @param factory 渲染后端Shader工厂
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool init(const std::string& shaderDir,
|
||||
const std::string& cacheDir,
|
||||
Ptr<IShaderFactory> factory);
|
||||
|
||||
/**
|
||||
* @brief 关闭Shader系统
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
* @return 已初始化返回true,否则返回false
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
/**
|
||||
* @brief 检查当前平台是否支持热重载
|
||||
* Switch平台使用romfs,不支持热重载
|
||||
* @return 支持热重载返回true
|
||||
*/
|
||||
bool isHotReloadSupported() const { return hotReloadSupported_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Shader加载
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 从分离文件加载Shader
|
||||
* @param name Shader名称
|
||||
* @param vertPath 顶点着色器文件路径
|
||||
* @param fragPath 片段着色器文件路径
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> loadFromFiles(const std::string& name,
|
||||
const std::string& vertPath,
|
||||
const std::string& fragPath);
|
||||
|
||||
/**
|
||||
* @brief 从组合文件加载Shader
|
||||
* @param path 组合Shader文件路径
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> loadFromCombinedFile(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 从源码加载Shader
|
||||
* @param name Shader名称
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> loadFromSource(const std::string& name,
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource);
|
||||
|
||||
/**
|
||||
* @brief 获取已加载的Shader
|
||||
* @param name Shader名称
|
||||
* @return Shader实例,不存在返回nullptr
|
||||
*/
|
||||
Ptr<IShader> get(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 检查Shader是否存在
|
||||
* @param name Shader名称
|
||||
* @return 存在返回true,否则返回false
|
||||
*/
|
||||
bool has(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 移除Shader
|
||||
* @param name Shader名称
|
||||
*/
|
||||
void remove(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 清除所有Shader
|
||||
*/
|
||||
void clear();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 热重载
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 注册重载回调
|
||||
* @param name Shader名称
|
||||
* @param callback 重载回调函数
|
||||
*/
|
||||
void setReloadCallback(const std::string& name, ShaderReloadCallback callback);
|
||||
|
||||
/**
|
||||
* @brief 启用/禁用热重载
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void setHotReloadEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief 检查热重载是否启用
|
||||
* @return 启用返回true,否则返回false
|
||||
*/
|
||||
bool isHotReloadEnabled() const;
|
||||
|
||||
/**
|
||||
* @brief 更新热重载系统(主循环调用)
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* @brief 手动重载Shader
|
||||
* @param name Shader名称
|
||||
* @return 重载成功返回true,失败返回false
|
||||
*/
|
||||
bool reload(const std::string& name);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 内置Shader
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取内置Shader
|
||||
* @param name 内置Shader名称
|
||||
* @return Shader实例
|
||||
*/
|
||||
Ptr<IShader> getBuiltin(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 加载所有内置Shader
|
||||
* @return 加载成功返回true,失败返回false
|
||||
*/
|
||||
bool loadBuiltinShaders();
|
||||
|
||||
/**
|
||||
* @brief 从JSON元数据文件加载Shader(多后端支持)
|
||||
* @param jsonPath JSON元数据文件路径
|
||||
* @param name Shader名称
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> loadFromMetadata(const std::string& jsonPath, const std::string& name);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 工具方法
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取Shader目录
|
||||
* @return Shader目录路径
|
||||
*/
|
||||
const std::string& getShaderDir() const { return shaderDir_; }
|
||||
|
||||
/**
|
||||
* @brief 获取ShaderLoader
|
||||
* @return ShaderLoader引用
|
||||
*/
|
||||
ShaderLoader& getLoader() { return loader_; }
|
||||
|
||||
private:
|
||||
ShaderManager() = default;
|
||||
~ShaderManager() = default;
|
||||
ShaderManager(const ShaderManager&) = delete;
|
||||
ShaderManager& operator=(const ShaderManager&) = delete;
|
||||
|
||||
std::string shaderDir_;
|
||||
std::string cacheDir_;
|
||||
Ptr<IShaderFactory> factory_;
|
||||
ShaderLoader loader_;
|
||||
|
||||
struct ShaderInfo {
|
||||
Ptr<IShader> shader;
|
||||
ShaderMetadata metadata;
|
||||
ShaderReloadCallback reloadCallback;
|
||||
std::string vertSource;
|
||||
std::string fragSource;
|
||||
std::vector<std::string> filePaths;
|
||||
};
|
||||
std::unordered_map<std::string, ShaderInfo> shaders_;
|
||||
|
||||
bool initialized_ = false;
|
||||
bool hotReloadEnabled_ = false;
|
||||
bool hotReloadSupported_ = true;
|
||||
|
||||
/**
|
||||
* @brief 从缓存加载Shader
|
||||
* @param name Shader名称
|
||||
* @param sourceHash 源码哈希值
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return Shader实例
|
||||
*/
|
||||
Ptr<IShader> loadFromCache(const std::string& name,
|
||||
const std::string& sourceHash,
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource);
|
||||
|
||||
/**
|
||||
* @brief 创建内置Shader源码
|
||||
*/
|
||||
void createBuiltinShaderSources();
|
||||
|
||||
/**
|
||||
* @brief 处理文件变化事件
|
||||
* @param shaderName Shader名称
|
||||
* @param event 文件变化事件
|
||||
*/
|
||||
void handleFileChange(const std::string& shaderName, const FileChangeEvent& event);
|
||||
};
|
||||
|
||||
// 便捷宏
|
||||
#define E2D_SHADER_MANAGER() ::extra2d::ShaderManager::getInstance()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/shader/shader_interface.h>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
struct WaterParams {
|
||||
float waveSpeed = 1.0f;
|
||||
float waveAmplitude = 0.02f;
|
||||
float waveFrequency = 4.0f;
|
||||
};
|
||||
|
||||
struct OutlineParams {
|
||||
Color color = Colors::Black;
|
||||
float thickness = 2.0f;
|
||||
};
|
||||
|
||||
struct DistortionParams {
|
||||
float distortionAmount = 0.02f;
|
||||
float timeScale = 1.0f;
|
||||
};
|
||||
|
||||
struct PixelateParams {
|
||||
float pixelSize = 8.0f;
|
||||
};
|
||||
|
||||
struct InvertParams {
|
||||
float strength = 1.0f;
|
||||
};
|
||||
|
||||
struct GrayscaleParams {
|
||||
float intensity = 1.0f;
|
||||
};
|
||||
|
||||
struct BlurParams {
|
||||
float radius = 5.0f;
|
||||
};
|
||||
|
||||
class ShaderPreset {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建水波纹效果着色器
|
||||
* @param params 水波纹效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> Water(const WaterParams& params = {});
|
||||
|
||||
/**
|
||||
* @brief 创建描边效果着色器
|
||||
* @param params 描边效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> Outline(const OutlineParams& params = {});
|
||||
|
||||
/**
|
||||
* @brief 创建扭曲效果着色器
|
||||
* @param params 扭曲效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> Distortion(const DistortionParams& params = {});
|
||||
|
||||
/**
|
||||
* @brief 创建像素化效果着色器
|
||||
* @param params 像素化效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> Pixelate(const PixelateParams& params = {});
|
||||
|
||||
/**
|
||||
* @brief 创建反相效果着色器
|
||||
* @param params 反相效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> Invert(const InvertParams& params = {});
|
||||
|
||||
/**
|
||||
* @brief 创建灰度效果着色器
|
||||
* @param params 灰度效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> Grayscale(const GrayscaleParams& params = {});
|
||||
|
||||
/**
|
||||
* @brief 创建模糊效果着色器
|
||||
* @param params 模糊效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> Blur(const BlurParams& params = {});
|
||||
|
||||
/**
|
||||
* @brief 创建灰度+描边组合效果着色器
|
||||
* @param grayParams 灰度效果参数
|
||||
* @param outlineParams 描边效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> GrayscaleOutline(const GrayscaleParams& grayParams,
|
||||
const OutlineParams& outlineParams);
|
||||
|
||||
/**
|
||||
* @brief 创建像素化+反相组合效果着色器
|
||||
* @param pixParams 像素化效果参数
|
||||
* @param invParams 反相效果参数
|
||||
* @return 配置好的着色器
|
||||
*/
|
||||
static Ptr<IShader> PixelateInvert(const PixelateParams& pixParams,
|
||||
const InvertParams& invParams);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,319 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/opengl/gl_shader.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
struct WaterParams {
|
||||
float waveSpeed = 1.0f;
|
||||
float waveAmplitude = 0.02f;
|
||||
float waveFrequency = 4.0f;
|
||||
};
|
||||
|
||||
struct OutlineParams {
|
||||
Color color = Colors::Black;
|
||||
float thickness = 2.0f;
|
||||
};
|
||||
|
||||
struct DistortionParams {
|
||||
float distortionAmount = 0.02f;
|
||||
float timeScale = 1.0f;
|
||||
};
|
||||
|
||||
struct PixelateParams {
|
||||
float pixelSize = 8.0f;
|
||||
};
|
||||
|
||||
struct InvertParams {
|
||||
float strength = 1.0f;
|
||||
};
|
||||
|
||||
struct GrayscaleParams {
|
||||
float intensity = 1.0f;
|
||||
};
|
||||
|
||||
struct BlurParams {
|
||||
float radius = 5.0f;
|
||||
};
|
||||
|
||||
namespace ShaderSource {
|
||||
|
||||
static const char* StandardVert = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
layout(location = 0) in vec2 a_position;
|
||||
layout(location = 1) in vec2 a_texCoord;
|
||||
layout(location = 2) in vec4 a_color;
|
||||
|
||||
uniform mat4 u_viewProjection;
|
||||
uniform mat4 u_model;
|
||||
|
||||
out vec2 v_texCoord;
|
||||
out vec4 v_color;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0);
|
||||
v_texCoord = a_texCoord;
|
||||
v_color = a_color;
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* StandardFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_opacity;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(u_texture, v_texCoord);
|
||||
fragColor = texColor * v_color;
|
||||
fragColor.a *= u_opacity;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* WaterFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_waveSpeed;
|
||||
uniform float u_waveAmplitude;
|
||||
uniform float u_waveFrequency;
|
||||
uniform float u_time;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec2 uv = v_texCoord;
|
||||
|
||||
// 水波纹效果
|
||||
float wave = sin(uv.y * u_waveFrequency + u_time * u_waveSpeed) * u_waveAmplitude;
|
||||
uv.x += wave;
|
||||
|
||||
vec4 texColor = texture(u_texture, uv);
|
||||
fragColor = texColor * v_color;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* OutlineFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform vec4 u_outlineColor;
|
||||
uniform float u_thickness;
|
||||
uniform vec2 u_textureSize;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(u_texture, v_texCoord);
|
||||
|
||||
// 简单的描边检测
|
||||
float alpha = 0.0;
|
||||
vec2 offset = u_thickness / u_textureSize;
|
||||
|
||||
alpha += texture(u_texture, v_texCoord + vec2(-offset.x, 0.0)).a;
|
||||
alpha += texture(u_texture, v_texCoord + vec2(offset.x, 0.0)).a;
|
||||
alpha += texture(u_texture, v_texCoord + vec2(0.0, -offset.y)).a;
|
||||
alpha += texture(u_texture, v_texCoord + vec2(0.0, offset.y)).a;
|
||||
|
||||
if (color.a < 0.1 && alpha > 0.0) {
|
||||
fragColor = u_outlineColor;
|
||||
} else {
|
||||
fragColor = color;
|
||||
}
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* DistortionFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_distortionAmount;
|
||||
uniform float u_time;
|
||||
uniform float u_timeScale;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec2 uv = v_texCoord;
|
||||
|
||||
// 扭曲效果
|
||||
float t = u_time * u_timeScale;
|
||||
float dx = sin(uv.y * 10.0 + t) * u_distortionAmount;
|
||||
float dy = cos(uv.x * 10.0 + t) * u_distortionAmount;
|
||||
uv += vec2(dx, dy);
|
||||
|
||||
vec4 texColor = texture(u_texture, uv);
|
||||
fragColor = texColor * v_color;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* PixelateFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_pixelSize;
|
||||
uniform vec2 u_textureSize;
|
||||
uniform float u_opacity;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec2 pixel = u_pixelSize / u_textureSize;
|
||||
vec2 uv = floor(v_texCoord / pixel) * pixel + pixel * 0.5;
|
||||
|
||||
vec4 texColor = texture(u_texture, uv);
|
||||
fragColor = texColor * v_color;
|
||||
fragColor.a *= u_opacity;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* InvertFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_strength;
|
||||
uniform float u_opacity;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(u_texture, v_texCoord) * v_color;
|
||||
vec3 inverted = vec3(1.0) - texColor.rgb;
|
||||
texColor.rgb = mix(texColor.rgb, inverted, u_strength);
|
||||
|
||||
fragColor = texColor;
|
||||
fragColor.a *= u_opacity;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* GrayscaleFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_intensity;
|
||||
uniform float u_opacity;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(u_texture, v_texCoord) * v_color;
|
||||
|
||||
float gray = dot(texColor.rgb, vec3(0.299, 0.587, 0.114));
|
||||
texColor.rgb = mix(texColor.rgb, vec3(gray), u_intensity);
|
||||
|
||||
fragColor = texColor;
|
||||
fragColor.a *= u_opacity;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* BlurFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_radius;
|
||||
uniform vec2 u_textureSize;
|
||||
uniform float u_opacity;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec2 texel = u_radius / u_textureSize;
|
||||
|
||||
vec4 sum = vec4(0.0);
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, -1.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, -1.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, -1.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, 0.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, 0.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, 0.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, 1.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, 1.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, 1.0));
|
||||
|
||||
vec4 texColor = sum / 9.0;
|
||||
fragColor = texColor * v_color;
|
||||
fragColor.a *= u_opacity;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
} // namespace ShaderSource
|
||||
|
||||
class ShaderPreset {
|
||||
public:
|
||||
static Ptr<GLShader> Water(const WaterParams& params);
|
||||
static Ptr<GLShader> Outline(const OutlineParams& params);
|
||||
static Ptr<GLShader> Distortion(const DistortionParams& params);
|
||||
static Ptr<GLShader> Pixelate(const PixelateParams& params);
|
||||
static Ptr<GLShader> Invert(const InvertParams& params);
|
||||
static Ptr<GLShader> Grayscale(const GrayscaleParams& params);
|
||||
static Ptr<GLShader> Blur(const BlurParams& params);
|
||||
|
||||
static Ptr<GLShader> GrayscaleOutline(const GrayscaleParams& grayParams,
|
||||
const OutlineParams& outlineParams);
|
||||
static Ptr<GLShader> PixelateInvert(const PixelateParams& pixParams,
|
||||
const InvertParams& invParams);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/opengl/gl_shader.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// Shader参数绑定回调
|
||||
// ============================================================================
|
||||
using ShaderBindCallback = std::function<void(GLShader &)>;
|
||||
|
||||
// ============================================================================
|
||||
// Shader系统 - 管理所有Shader的加载、缓存和热重载
|
||||
// ============================================================================
|
||||
class ShaderSystem {
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
// 单例访问
|
||||
// ------------------------------------------------------------------------
|
||||
static ShaderSystem &getInstance();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 初始化和关闭
|
||||
// ------------------------------------------------------------------------
|
||||
bool init();
|
||||
void shutdown();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Shader加载
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 从文件加载Shader
|
||||
* @param name Shader名称(用于缓存)
|
||||
* @param vertPath 顶点着色器文件路径
|
||||
* @param fragPath 片段着色器文件路径
|
||||
* @return 加载的Shader,失败返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> loadFromFile(const std::string &name,
|
||||
const std::string &vertPath,
|
||||
const std::string &fragPath);
|
||||
|
||||
/**
|
||||
* @brief 从源码字符串加载Shader
|
||||
* @param name Shader名称(用于缓存)
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 加载的Shader,失败返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> loadFromSource(const std::string &name,
|
||||
const std::string &vertSource,
|
||||
const std::string &fragSource);
|
||||
|
||||
/**
|
||||
* @brief 从已编译的程序获取Shader
|
||||
* @param name Shader名称
|
||||
* @return 缓存的Shader,不存在返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> get(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief 检查Shader是否存在
|
||||
*/
|
||||
bool has(const std::string &name) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Shader移除
|
||||
// ------------------------------------------------------------------------
|
||||
void remove(const std::string &name);
|
||||
void clear();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 热重载支持
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 启用/禁用文件监视
|
||||
*/
|
||||
void setFileWatching(bool enable);
|
||||
bool isFileWatching() const { return fileWatching_; }
|
||||
|
||||
/**
|
||||
* @brief 更新文件监视(在主循环中调用)
|
||||
*/
|
||||
void updateFileWatching();
|
||||
|
||||
/**
|
||||
* @brief 重载指定Shader
|
||||
*/
|
||||
bool reload(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief 重载所有Shader
|
||||
*/
|
||||
void reloadAll();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 内置Shader获取
|
||||
// ------------------------------------------------------------------------
|
||||
Ptr<GLShader> getBuiltinSpriteShader();
|
||||
Ptr<GLShader> getBuiltinParticleShader();
|
||||
Ptr<GLShader> getBuiltinPostProcessShader();
|
||||
Ptr<GLShader> getBuiltinShapeShader();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 工具方法
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 从文件读取文本内容
|
||||
*/
|
||||
static std::string readFile(const std::string &filepath);
|
||||
|
||||
/**
|
||||
* @brief 获取Shader文件的最后修改时间
|
||||
*/
|
||||
static uint64_t getFileModifiedTime(const std::string &filepath);
|
||||
|
||||
private:
|
||||
ShaderSystem() = default;
|
||||
~ShaderSystem() = default;
|
||||
ShaderSystem(const ShaderSystem &) = delete;
|
||||
ShaderSystem &operator=(const ShaderSystem &) = delete;
|
||||
|
||||
struct ShaderInfo {
|
||||
Ptr<GLShader> shader;
|
||||
std::string vertPath;
|
||||
std::string fragPath;
|
||||
uint64_t vertModifiedTime;
|
||||
uint64_t fragModifiedTime;
|
||||
bool isBuiltin;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, ShaderInfo> shaders_;
|
||||
bool fileWatching_ = false;
|
||||
float watchTimer_ = 0.0f;
|
||||
static constexpr float WATCH_INTERVAL = 1.0f; // 检查间隔(秒)
|
||||
|
||||
// 内置Shader缓存
|
||||
Ptr<GLShader> builtinSpriteShader_;
|
||||
Ptr<GLShader> builtinParticleShader_;
|
||||
Ptr<GLShader> builtinPostProcessShader_;
|
||||
Ptr<GLShader> builtinShapeShader_;
|
||||
|
||||
bool loadBuiltinShaders();
|
||||
void checkAndReload();
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Shader参数包装器 - 简化Uniform设置
|
||||
// ============================================================================
|
||||
class ShaderParams {
|
||||
public:
|
||||
explicit ShaderParams(GLShader &shader);
|
||||
|
||||
ShaderParams &setBool(const std::string &name, bool value);
|
||||
ShaderParams &setInt(const std::string &name, int value);
|
||||
ShaderParams &setFloat(const std::string &name, float value);
|
||||
ShaderParams &setVec2(const std::string &name, const glm::vec2 &value);
|
||||
ShaderParams &setVec3(const std::string &name, const glm::vec3 &value);
|
||||
ShaderParams &setVec4(const std::string &name, const glm::vec4 &value);
|
||||
ShaderParams &setMat4(const std::string &name, const glm::mat4 &value);
|
||||
ShaderParams &setColor(const std::string &name, const Color &color);
|
||||
|
||||
private:
|
||||
GLShader &shader_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 便捷宏
|
||||
// ============================================================================
|
||||
#define E2D_SHADER_SYSTEM() ::extra2d::ShaderSystem::getInstance()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -3,8 +3,8 @@
|
|||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
|
@ -146,9 +146,9 @@ private:
|
|||
/**
|
||||
* @brief 全局图集管理器(单例)
|
||||
*/
|
||||
class TextureAtlasManager {
|
||||
class TextureAtlasMgr {
|
||||
public:
|
||||
static TextureAtlasManager& getInstance();
|
||||
static TextureAtlasMgr& get();
|
||||
|
||||
// 获取主图集
|
||||
TextureAtlas& getAtlas() { return atlas_; }
|
||||
|
|
@ -172,11 +172,11 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
TextureAtlasManager() = default;
|
||||
~TextureAtlasManager() = default;
|
||||
TextureAtlasMgr() = default;
|
||||
~TextureAtlasMgr() = default;
|
||||
|
||||
TextureAtlasManager(const TextureAtlasManager&) = delete;
|
||||
TextureAtlasManager& operator=(const TextureAtlasManager&) = delete;
|
||||
TextureAtlasMgr(const TextureAtlasMgr&) = delete;
|
||||
TextureAtlasMgr& operator=(const TextureAtlasMgr&) = delete;
|
||||
|
||||
TextureAtlas atlas_;
|
||||
};
|
||||
|
|
@ -0,0 +1,561 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Scene;
|
||||
class RenderBackend;
|
||||
|
||||
// ============================================================================
|
||||
// 纹理加载选项
|
||||
// ============================================================================
|
||||
struct TextureLoadOptions {
|
||||
bool generateMipmaps = true; // 是否生成 mipmaps
|
||||
bool sRGB = true; // 是否使用 sRGB 色彩空间
|
||||
bool premultiplyAlpha = false; // 是否预乘 Alpha
|
||||
PixelFormat preferredFormat = PixelFormat::RGBA8; // 首选像素格式
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 纹理键 - 用于唯一标识纹理缓存条目
|
||||
// ============================================================================
|
||||
struct TextureKey {
|
||||
std::string path; // 纹理文件路径
|
||||
Rect region; // 纹理区域(用于纹理图集)
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
TextureKey() = default;
|
||||
|
||||
/**
|
||||
* @brief 构造函数(仅路径)
|
||||
* @param p 纹理文件路径
|
||||
*/
|
||||
explicit TextureKey(const std::string &p) : path(p), region(Rect::Zero()) {}
|
||||
|
||||
/**
|
||||
* @brief 构造函数(路径 + 区域)
|
||||
* @param p 纹理文件路径
|
||||
* @param r 纹理区域
|
||||
*/
|
||||
TextureKey(const std::string &p, const Rect &r) : path(p), region(r) {}
|
||||
|
||||
/**
|
||||
* @brief 相等比较运算符
|
||||
* @param other 另一个 TextureKey
|
||||
* @return 是否相等
|
||||
*/
|
||||
bool operator==(const TextureKey &other) const {
|
||||
return path == other.path && region == other.region;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 不等比较运算符
|
||||
* @param other 另一个 TextureKey
|
||||
* @return 是否不等
|
||||
*/
|
||||
bool operator!=(const TextureKey &other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// TextureKey 哈希函子
|
||||
// ============================================================================
|
||||
struct TextureKeyHash {
|
||||
/**
|
||||
* @brief 计算 TextureKey 的哈希值
|
||||
* @param key 纹理键
|
||||
* @return 哈希值
|
||||
*/
|
||||
size_t operator()(const TextureKey &key) const {
|
||||
size_t h1 = std::hash<std::string>{}(key.path);
|
||||
size_t h2 = std::hash<float>{}(key.region.origin.x);
|
||||
size_t h3 = std::hash<float>{}(key.region.origin.y);
|
||||
size_t h4 = std::hash<float>{}(key.region.size.width);
|
||||
size_t h5 = std::hash<float>{}(key.region.size.height);
|
||||
|
||||
// 组合哈希值
|
||||
size_t result = h1;
|
||||
result ^= h2 + 0x9e3779b9 + (result << 6) + (result >> 2);
|
||||
result ^= h3 + 0x9e3779b9 + (result << 6) + (result >> 2);
|
||||
result ^= h4 + 0x9e3779b9 + (result << 6) + (result >> 2);
|
||||
result ^= h5 + 0x9e3779b9 + (result << 6) + (result >> 2);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 纹理池条目
|
||||
// ============================================================================
|
||||
struct TexturePoolEntry {
|
||||
Ptr<Texture> texture; // 纹理对象
|
||||
mutable std::atomic<uint32_t> refCount; // 引用计数
|
||||
TextureKey key; // 纹理键
|
||||
size_t memorySize; // 内存占用(字节)
|
||||
mutable uint64_t lastAccessTime; // 最后访问时间戳
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
TexturePoolEntry()
|
||||
: texture(nullptr), refCount(0), key(), memorySize(0), lastAccessTime(0) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param tex 纹理对象
|
||||
* @param k 纹理键
|
||||
* @param memSize 内存占用
|
||||
*/
|
||||
TexturePoolEntry(Ptr<Texture> tex, const TextureKey &k, size_t memSize)
|
||||
: texture(tex), refCount(1), key(k), memorySize(memSize),
|
||||
lastAccessTime(getCurrentTime()) {}
|
||||
|
||||
/**
|
||||
* @brief 移动构造函数
|
||||
* @param other 另一个条目
|
||||
*/
|
||||
TexturePoolEntry(TexturePoolEntry &&other) noexcept
|
||||
: texture(std::move(other.texture)),
|
||||
refCount(other.refCount.load(std::memory_order_relaxed)),
|
||||
key(std::move(other.key)), memorySize(other.memorySize),
|
||||
lastAccessTime(other.lastAccessTime) {}
|
||||
|
||||
/**
|
||||
* @brief 移动赋值运算符
|
||||
* @param other 另一个条目
|
||||
* @return 引用
|
||||
*/
|
||||
TexturePoolEntry &operator=(TexturePoolEntry &&other) noexcept {
|
||||
if (this != &other) {
|
||||
texture = std::move(other.texture);
|
||||
refCount.store(other.refCount.load(std::memory_order_relaxed),
|
||||
std::memory_order_relaxed);
|
||||
key = std::move(other.key);
|
||||
memorySize = other.memorySize;
|
||||
lastAccessTime = other.lastAccessTime;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// 禁止拷贝
|
||||
TexturePoolEntry(const TexturePoolEntry &) = delete;
|
||||
TexturePoolEntry &operator=(const TexturePoolEntry &) = delete;
|
||||
|
||||
/**
|
||||
* @brief 更新最后访问时间
|
||||
*/
|
||||
void touch() const { lastAccessTime = getCurrentTime(); }
|
||||
|
||||
/**
|
||||
* @brief 获取当前时间戳
|
||||
* @return 时间戳(毫秒)
|
||||
*/
|
||||
static uint64_t getCurrentTime() {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now.time_since_epoch());
|
||||
return static_cast<uint64_t>(duration.count());
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 纹理引用智能指针 - 自动管理纹理池引用计数
|
||||
// ============================================================================
|
||||
class TextureRef {
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
TextureRef() : texture_(nullptr), entry_(nullptr), mutex_(nullptr) {}
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param texture 纹理对象
|
||||
* @param entry 纹理池条目
|
||||
* @param mutex 互斥锁
|
||||
*/
|
||||
TextureRef(Ptr<Texture> texture, TexturePoolEntry *entry, std::mutex *mutex)
|
||||
: texture_(texture), entry_(entry), mutex_(mutex) {}
|
||||
|
||||
/**
|
||||
* @brief 创建独立的纹理引用(不管理引用计数)
|
||||
* @param texture 纹理对象
|
||||
* @return 独立的纹理引用
|
||||
*/
|
||||
static TextureRef fromTexture(Ptr<Texture> texture) {
|
||||
return TextureRef(texture, nullptr, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 拷贝构造函数
|
||||
* @param other 另一个 TextureRef
|
||||
*/
|
||||
TextureRef(const TextureRef &other)
|
||||
: texture_(other.texture_), entry_(other.entry_), mutex_(other.mutex_) {
|
||||
if (entry_ && entry_->refCount.load(std::memory_order_relaxed) > 0) {
|
||||
entry_->refCount.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移动构造函数
|
||||
* @param other 另一个 TextureRef
|
||||
*/
|
||||
TextureRef(TextureRef &&other) noexcept
|
||||
: texture_(std::move(other.texture_)), entry_(other.entry_),
|
||||
mutex_(other.mutex_) {
|
||||
other.entry_ = nullptr;
|
||||
other.mutex_ = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~TextureRef() { reset(); }
|
||||
|
||||
/**
|
||||
* @brief 拷贝赋值运算符
|
||||
* @param other 另一个 TextureRef
|
||||
* @return 引用
|
||||
*/
|
||||
TextureRef &operator=(const TextureRef &other) {
|
||||
if (this != &other) {
|
||||
reset();
|
||||
texture_ = other.texture_;
|
||||
entry_ = other.entry_;
|
||||
mutex_ = other.mutex_;
|
||||
if (entry_ && entry_->refCount.load(std::memory_order_relaxed) > 0) {
|
||||
entry_->refCount.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移动赋值运算符
|
||||
* @param other 另一个 TextureRef
|
||||
* @return 引用
|
||||
*/
|
||||
TextureRef &operator=(TextureRef &&other) noexcept {
|
||||
if (this != &other) {
|
||||
reset();
|
||||
texture_ = std::move(other.texture_);
|
||||
entry_ = other.entry_;
|
||||
mutex_ = other.mutex_;
|
||||
other.entry_ = nullptr;
|
||||
other.mutex_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 重置引用
|
||||
*/
|
||||
void reset() {
|
||||
if (entry_ && mutex_) {
|
||||
std::lock_guard<std::mutex> lock(*mutex_);
|
||||
if (entry_->refCount.load(std::memory_order_relaxed) > 0) {
|
||||
entry_->refCount.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
texture_.reset();
|
||||
entry_ = nullptr;
|
||||
mutex_ = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取纹理对象
|
||||
* @return 纹理对象指针
|
||||
*/
|
||||
Texture *get() const { return texture_.get(); }
|
||||
|
||||
/**
|
||||
* @brief 获取纹理对象(智能指针)
|
||||
* @return 纹理对象智能指针
|
||||
*/
|
||||
Ptr<Texture> getPtr() const { return texture_; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否有效
|
||||
* @return 是否有效
|
||||
*/
|
||||
bool valid() const { return texture_ != nullptr; }
|
||||
|
||||
/**
|
||||
* @brief 布尔转换运算符
|
||||
*/
|
||||
explicit operator bool() const { return valid(); }
|
||||
|
||||
/**
|
||||
* @brief 箭头运算符
|
||||
*/
|
||||
Texture *operator->() const { return texture_.get(); }
|
||||
|
||||
/**
|
||||
* @brief 解引用运算符
|
||||
*/
|
||||
Texture &operator*() const { return *texture_; }
|
||||
|
||||
private:
|
||||
Ptr<Texture> texture_;
|
||||
TexturePoolEntry *entry_;
|
||||
std::mutex *mutex_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 纹理池 - 纹理缓存和内存管理系统
|
||||
// 特性:
|
||||
// - 纹理缓存和复用
|
||||
// - 引用计数管理
|
||||
// - 内存使用限制
|
||||
// - LRU 淘汰策略
|
||||
// - 线程安全
|
||||
// ============================================================================
|
||||
class TexturePool {
|
||||
public:
|
||||
// ========================================================================
|
||||
// 统计信息
|
||||
// ========================================================================
|
||||
struct Stats {
|
||||
size_t textureCount = 0; // 纹理数量
|
||||
size_t memoryUsage = 0; // 内存使用量(字节)
|
||||
size_t maxMemoryUsage = 0; // 最大内存使用量
|
||||
size_t cacheHits = 0; // 缓存命中次数
|
||||
size_t cacheMisses = 0; // 缓存未命中次数
|
||||
size_t evictionCount = 0; // 淘汰次数
|
||||
};
|
||||
|
||||
// ========================================================================
|
||||
// 构造和析构
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
TexturePool();
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param scene 场景指针
|
||||
* @param maxMemoryUsage 最大内存使用量(0 表示无限制)
|
||||
*/
|
||||
explicit TexturePool(Scene *scene, size_t maxMemoryUsage = 0);
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~TexturePool();
|
||||
|
||||
// 禁止拷贝
|
||||
TexturePool(const TexturePool &) = delete;
|
||||
TexturePool &operator=(const TexturePool &) = delete;
|
||||
|
||||
/**
|
||||
* @brief 初始化纹理池
|
||||
* @param scene 场景指针
|
||||
* @param maxMemoryUsage 最大内存使用量(0 表示无限制)
|
||||
*/
|
||||
void init(Scene *scene, size_t maxMemoryUsage = 0);
|
||||
|
||||
// ========================================================================
|
||||
// 纹理加载
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 从文件加载纹理
|
||||
* @param path 文件路径
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*/
|
||||
TextureRef load(const std::string &path,
|
||||
const TextureLoadOptions &options = TextureLoadOptions());
|
||||
|
||||
/**
|
||||
* @brief 从文件加载纹理区域
|
||||
* @param path 文件路径
|
||||
* @param region 纹理区域
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*/
|
||||
TextureRef load(const std::string &path, const Rect ®ion,
|
||||
const TextureLoadOptions &options = TextureLoadOptions());
|
||||
|
||||
/**
|
||||
* @brief 从内存加载纹理
|
||||
* @param data 像素数据
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param channels 通道数
|
||||
* @param key 缓存键
|
||||
* @return 纹理引用
|
||||
*/
|
||||
TextureRef loadFromMemory(const uint8_t *data, int width, int height,
|
||||
int channels, const std::string &key);
|
||||
|
||||
/**
|
||||
* @brief 获取或加载纹理
|
||||
* @param path 文件路径
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*/
|
||||
TextureRef
|
||||
getOrLoad(const std::string &path,
|
||||
const TextureLoadOptions &options = TextureLoadOptions());
|
||||
|
||||
/**
|
||||
* @brief 获取或加载纹理区域
|
||||
* @param path 文件路径
|
||||
* @param region 纹理区域
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*/
|
||||
TextureRef
|
||||
getOrLoad(const std::string &path, const Rect ®ion,
|
||||
const TextureLoadOptions &options = TextureLoadOptions());
|
||||
|
||||
// ========================================================================
|
||||
// 引用计数管理
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 增加引用计数
|
||||
* @param key 纹理键
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool addRef(const TextureKey &key);
|
||||
|
||||
/**
|
||||
* @brief 减少引用计数
|
||||
* @param key 纹理键
|
||||
* @return 减少后的引用计数
|
||||
*/
|
||||
uint32_t release(const TextureKey &key);
|
||||
|
||||
/**
|
||||
* @brief 获取引用计数
|
||||
* @param key 纹理键
|
||||
* @return 引用计数
|
||||
*/
|
||||
uint32_t getRefCount(const TextureKey &key) const;
|
||||
|
||||
// ========================================================================
|
||||
// 缓存管理
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 检查纹理是否已缓存
|
||||
* @param key 纹理键
|
||||
* @return 是否已缓存
|
||||
*/
|
||||
bool isCached(const TextureKey &key) const;
|
||||
|
||||
/**
|
||||
* @brief 从缓存中移除纹理
|
||||
* @param key 纹理键
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool removeFromCache(const TextureKey &key);
|
||||
|
||||
/**
|
||||
* @brief 垃圾回收(移除引用计数为 0 的纹理)
|
||||
* @return 移除的纹理数量
|
||||
*/
|
||||
size_t collectGarbage();
|
||||
|
||||
/**
|
||||
* @brief 清空所有缓存
|
||||
*/
|
||||
void clear();
|
||||
|
||||
// ========================================================================
|
||||
// 内存管理
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取当前内存使用量
|
||||
* @return 内存使用量(字节)
|
||||
*/
|
||||
size_t getMemoryUsage() const;
|
||||
|
||||
/**
|
||||
* @brief 设置最大内存使用量
|
||||
* @param maxMemory 最大内存使用量(0 表示无限制)
|
||||
*/
|
||||
void setMaxMemoryUsage(size_t maxMemory);
|
||||
|
||||
/**
|
||||
* @brief 获取最大内存使用量
|
||||
* @return 最大内存使用量
|
||||
*/
|
||||
size_t getMaxMemoryUsage() const { return maxMemoryUsage_; }
|
||||
|
||||
/**
|
||||
* @brief 执行 LRU 淘汰
|
||||
* @param targetMemory 目标内存使用量
|
||||
* @return 淘汰的纹理数量
|
||||
*/
|
||||
size_t evictLRU(size_t targetMemory = 0);
|
||||
|
||||
// ========================================================================
|
||||
// 统计信息
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* @brief 获取统计信息
|
||||
* @return 统计信息
|
||||
*/
|
||||
Stats getStats() const;
|
||||
|
||||
/**
|
||||
* @brief 重置统计信息
|
||||
*/
|
||||
void resetStats();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 计算纹理内存大小
|
||||
* @param texture 纹理对象
|
||||
* @return 内存大小(字节)
|
||||
*/
|
||||
static size_t calculateTextureMemory(const Texture *texture);
|
||||
|
||||
/**
|
||||
* @brief 检查是否需要淘汰
|
||||
* @return 是否需要淘汰
|
||||
*/
|
||||
bool needsEviction() const;
|
||||
|
||||
/**
|
||||
* @brief 尝试自动淘汰
|
||||
*/
|
||||
void tryAutoEvict();
|
||||
|
||||
Scene *scene_; // 场景指针
|
||||
mutable std::mutex mutex_; // 互斥锁
|
||||
std::unordered_map<TextureKey, TexturePoolEntry, TextureKeyHash>
|
||||
cache_; // 纹理缓存
|
||||
|
||||
size_t maxMemoryUsage_; // 最大内存使用量
|
||||
size_t currentMemoryUsage_; // 当前内存使用量
|
||||
|
||||
// 统计信息
|
||||
mutable std::atomic<size_t> cacheHits_;
|
||||
mutable std::atomic<size_t> cacheMisses_;
|
||||
mutable std::atomic<size_t> evictionCount_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <extra2d/platform/iinput.h>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 平台后端工厂
|
||||
* 用于注册和创建平台后端
|
||||
*/
|
||||
class BackendFactory {
|
||||
public:
|
||||
using WindowFn = std::function<UniquePtr<IWindow>()>;
|
||||
using InputFn = std::function<UniquePtr<IInput>()>;
|
||||
|
||||
/**
|
||||
* @brief 注册平台后端
|
||||
* @param name 后端名称
|
||||
* @param win 窗口创建函数
|
||||
* @param in 输入创建函数
|
||||
*/
|
||||
static void reg(const std::string& name, WindowFn win, InputFn in);
|
||||
|
||||
/**
|
||||
* @brief 创建窗口实例
|
||||
* @param name 后端名称
|
||||
* @return 窗口实例,如果后端不存在返回 nullptr
|
||||
*/
|
||||
static UniquePtr<IWindow> createWindow(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 创建输入实例
|
||||
* @param name 后端名称
|
||||
* @return 输入实例,如果后端不存在返回 nullptr
|
||||
*/
|
||||
static UniquePtr<IInput> createInput(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 获取所有已注册的后端名称
|
||||
*/
|
||||
static std::vector<std::string> backends();
|
||||
|
||||
/**
|
||||
* @brief 检查后端是否存在
|
||||
*/
|
||||
static bool has(const std::string& name);
|
||||
|
||||
private:
|
||||
struct BackendEntry {
|
||||
WindowFn windowFn;
|
||||
InputFn inputFn;
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, BackendEntry>& registry();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 平台后端注册宏
|
||||
* 在全局作用域使用此宏注册平台后端
|
||||
*
|
||||
* @example
|
||||
* E2D_REG_BACKEND(sdl2, SDL2Window, SDL2Input)
|
||||
*/
|
||||
#define E2D_REG_BACKEND(name, WinClass, InClass) \
|
||||
namespace { \
|
||||
__attribute__((used)) \
|
||||
static struct E2D_BACKEND_REG_##name { \
|
||||
E2D_BACKEND_REG_##name() { \
|
||||
::extra2d::BackendFactory::reg( \
|
||||
#name, \
|
||||
[]() -> ::extra2d::UniquePtr<::extra2d::IWindow> { \
|
||||
return ::extra2d::makeUnique<WinClass>(); \
|
||||
}, \
|
||||
[]() -> ::extra2d::UniquePtr<::extra2d::IInput> { \
|
||||
return ::extra2d::makeUnique<InClass>(); \
|
||||
} \
|
||||
); \
|
||||
} \
|
||||
} e2d_backend_reg_##name; \
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/platform/keys.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 触摸点信息
|
||||
*/
|
||||
struct TouchPoint {
|
||||
int id = 0;
|
||||
Vec2 position;
|
||||
Vec2 delta;
|
||||
bool pressed = false;
|
||||
bool released = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 输入抽象接口
|
||||
* 所有平台输入后端必须实现此接口
|
||||
*/
|
||||
class IInput {
|
||||
public:
|
||||
virtual ~IInput() = default;
|
||||
|
||||
/**
|
||||
* @brief 初始化输入系统
|
||||
*/
|
||||
virtual void init() = 0;
|
||||
|
||||
/**
|
||||
* @brief 关闭输入系统
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/**
|
||||
* @brief 每帧更新输入状态
|
||||
*/
|
||||
virtual void update() = 0;
|
||||
|
||||
// ========== 键盘 ==========
|
||||
|
||||
/**
|
||||
* @brief 检测按键是否按下(持续状态)
|
||||
*/
|
||||
virtual bool down(Key key) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检测按键是否刚按下(仅当前帧)
|
||||
*/
|
||||
virtual bool pressed(Key key) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检测按键是否刚释放(仅当前帧)
|
||||
*/
|
||||
virtual bool released(Key key) const = 0;
|
||||
|
||||
// ========== 鼠标 ==========
|
||||
|
||||
/**
|
||||
* @brief 检测鼠标按钮是否按下
|
||||
*/
|
||||
virtual bool down(Mouse btn) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检测鼠标按钮是否刚按下
|
||||
*/
|
||||
virtual bool pressed(Mouse btn) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检测鼠标按钮是否刚释放
|
||||
*/
|
||||
virtual bool released(Mouse btn) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取鼠标位置
|
||||
*/
|
||||
virtual Vec2 mouse() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取鼠标移动增量
|
||||
*/
|
||||
virtual Vec2 mouseDelta() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取滚轮值
|
||||
*/
|
||||
virtual float scroll() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取滚轮增量
|
||||
*/
|
||||
virtual float scrollDelta() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置鼠标位置
|
||||
*/
|
||||
virtual void setMouse(const Vec2& pos) = 0;
|
||||
|
||||
// ========== 手柄 ==========
|
||||
|
||||
/**
|
||||
* @brief 检测手柄是否连接
|
||||
*/
|
||||
virtual bool gamepad() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检测手柄按钮是否按下
|
||||
*/
|
||||
virtual bool down(Gamepad btn) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检测手柄按钮是否刚按下
|
||||
*/
|
||||
virtual bool pressed(Gamepad btn) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检测手柄按钮是否刚释放
|
||||
*/
|
||||
virtual bool released(Gamepad btn) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取左摇杆值
|
||||
*/
|
||||
virtual Vec2 leftStick() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取右摇杆值
|
||||
*/
|
||||
virtual Vec2 rightStick() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取左扳机值
|
||||
*/
|
||||
virtual float leftTrigger() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取右扳机值
|
||||
*/
|
||||
virtual float rightTrigger() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置手柄振动
|
||||
* @param left 左马达强度 [0, 1]
|
||||
* @param right 右马达强度 [0, 1]
|
||||
*/
|
||||
virtual void vibrate(float left, float right) = 0;
|
||||
|
||||
// ========== 触摸 ==========
|
||||
|
||||
/**
|
||||
* @brief 检测是否有触摸
|
||||
*/
|
||||
virtual bool touching() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取触摸点数量
|
||||
*/
|
||||
virtual int touchCount() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取触摸点位置
|
||||
* @param index 触摸点索引
|
||||
*/
|
||||
virtual Vec2 touch(int index) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取触摸点信息
|
||||
* @param index 触摸点索引
|
||||
*/
|
||||
virtual TouchPoint touchPoint(int index) const = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/event/input_codes.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 鼠标按钮枚举
|
||||
// ============================================================================
|
||||
enum class MouseButton {
|
||||
Left = 0,
|
||||
Right = 1,
|
||||
Middle = 2,
|
||||
Button4 = 3,
|
||||
Button5 = 4,
|
||||
Button6 = 5,
|
||||
Button7 = 6,
|
||||
Button8 = 7,
|
||||
Count = 8
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Input 类 - 跨平台输入管理
|
||||
// 支持: 键盘、鼠标、手柄、触摸屏
|
||||
// ============================================================================
|
||||
class Input {
|
||||
public:
|
||||
Input();
|
||||
~Input();
|
||||
|
||||
// 初始化
|
||||
void init();
|
||||
void shutdown();
|
||||
|
||||
// 每帧更新
|
||||
void update();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 键盘输入
|
||||
// ------------------------------------------------------------------------
|
||||
bool isKeyDown(int keyCode) const;
|
||||
bool isKeyPressed(int keyCode) const;
|
||||
bool isKeyReleased(int keyCode) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 手柄按钮
|
||||
// ------------------------------------------------------------------------
|
||||
bool isButtonDown(int button) const;
|
||||
bool isButtonPressed(int button) const;
|
||||
bool isButtonReleased(int button) const;
|
||||
|
||||
// 摇杆
|
||||
Vec2 getLeftStick() const;
|
||||
Vec2 getRightStick() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 鼠标输入
|
||||
// ------------------------------------------------------------------------
|
||||
bool isMouseDown(MouseButton button) const;
|
||||
bool isMousePressed(MouseButton button) const;
|
||||
bool isMouseReleased(MouseButton button) const;
|
||||
|
||||
Vec2 getMousePosition() const;
|
||||
Vec2 getMouseDelta() const;
|
||||
float getMouseScroll() const { return mouseScroll_; }
|
||||
float getMouseScrollDelta() const { return mouseScroll_ - prevMouseScroll_; }
|
||||
|
||||
void setMousePosition(const Vec2 &position);
|
||||
void setMouseVisible(bool visible);
|
||||
void setMouseLocked(bool locked);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 触摸屏 (Switch 原生支持,PC 端模拟或禁用)
|
||||
// ------------------------------------------------------------------------
|
||||
bool isTouching() const { return touching_; }
|
||||
Vec2 getTouchPosition() const { return touchPosition_; }
|
||||
int getTouchCount() const { return touchCount_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 便捷方法
|
||||
// ------------------------------------------------------------------------
|
||||
bool isAnyKeyDown() const;
|
||||
bool isAnyMouseDown() const;
|
||||
|
||||
private:
|
||||
static constexpr int MAX_BUTTONS = SDL_CONTROLLER_BUTTON_MAX;
|
||||
static constexpr int MAX_KEYS = SDL_NUM_SCANCODES;
|
||||
|
||||
SDL_GameController *controller_;
|
||||
|
||||
// 键盘状态 (PC 端使用)
|
||||
std::array<bool, MAX_KEYS> keysDown_;
|
||||
std::array<bool, MAX_KEYS> prevKeysDown_;
|
||||
|
||||
// 手柄按钮状态
|
||||
std::array<bool, MAX_BUTTONS> buttonsDown_;
|
||||
std::array<bool, MAX_BUTTONS> prevButtonsDown_;
|
||||
|
||||
// 摇杆状态
|
||||
float leftStickX_;
|
||||
float leftStickY_;
|
||||
float rightStickX_;
|
||||
float rightStickY_;
|
||||
|
||||
// 鼠标状态 (PC 端使用)
|
||||
Vec2 mousePosition_;
|
||||
Vec2 prevMousePosition_;
|
||||
float mouseScroll_;
|
||||
float prevMouseScroll_;
|
||||
std::array<bool, 8> mouseButtonsDown_;
|
||||
std::array<bool, 8> prevMouseButtonsDown_;
|
||||
|
||||
// 触摸屏状态 (Switch 原生)
|
||||
bool touching_;
|
||||
bool prevTouching_;
|
||||
Vec2 touchPosition_;
|
||||
Vec2 prevTouchPosition_;
|
||||
int touchCount_;
|
||||
|
||||
// 映射键盘 keyCode 到 SDL GameController 按钮 (Switch 兼容模式)
|
||||
SDL_GameControllerButton mapKeyToButton(int keyCode) const;
|
||||
|
||||
// 更新键盘状态
|
||||
void updateKeyboard();
|
||||
|
||||
// 更新鼠标状态
|
||||
void updateMouse();
|
||||
|
||||
// 更新手柄状态
|
||||
void updateGamepad();
|
||||
|
||||
// 更新触摸屏状态
|
||||
void updateTouch();
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/platform/iinput.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <functional>
|
||||
#include <typeindex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 输入模块配置结构
|
||||
*/
|
||||
struct InputCfg {
|
||||
float deadzone;
|
||||
float mouseSensitivity;
|
||||
bool enableVibration;
|
||||
int maxGamepads;
|
||||
int priority;
|
||||
|
||||
InputCfg()
|
||||
: deadzone(0.15f)
|
||||
, mouseSensitivity(1.0f)
|
||||
, enableVibration(true)
|
||||
, maxGamepads(4)
|
||||
, priority(20)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 输入模块
|
||||
* 管理输入设备
|
||||
*/
|
||||
class InputModule : public Module {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数(Lambda 配置)
|
||||
* @param configFn 配置函数
|
||||
*/
|
||||
explicit InputModule(std::function<void(InputCfg&)> configFn);
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~InputModule() override;
|
||||
|
||||
bool init() override;
|
||||
void shutdown() override;
|
||||
bool ok() const override { return initialized_; }
|
||||
const char* name() const override { return "input"; }
|
||||
int priority() const override { return cfg_.priority; }
|
||||
|
||||
/**
|
||||
* @brief 获取依赖
|
||||
* @return 依赖模块类型列表
|
||||
*/
|
||||
std::vector<std::type_index> deps() const override {
|
||||
return {std::type_index(typeid(WindowModule))};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取输入接口
|
||||
* @return 输入接口指针
|
||||
*/
|
||||
IInput* input() const { return input_; }
|
||||
|
||||
/**
|
||||
* @brief 更新输入状态
|
||||
*/
|
||||
void update();
|
||||
|
||||
private:
|
||||
InputCfg cfg_;
|
||||
IInput* input_ = nullptr;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/platform/window_config.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class IInput;
|
||||
|
||||
/**
|
||||
* @brief 光标形状
|
||||
*/
|
||||
enum class Cursor {
|
||||
Arrow,
|
||||
IBeam,
|
||||
Crosshair,
|
||||
Hand,
|
||||
HResize,
|
||||
VResize,
|
||||
Hidden
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 窗口抽象接口
|
||||
* 所有平台窗口后端必须实现此接口
|
||||
*/
|
||||
class IWindow {
|
||||
public:
|
||||
virtual ~IWindow() = default;
|
||||
|
||||
/**
|
||||
* @brief 创建窗口
|
||||
* @param cfg 窗口配置
|
||||
* @return 创建是否成功
|
||||
*/
|
||||
virtual bool create(const WindowConfigData& cfg) = 0;
|
||||
|
||||
/**
|
||||
* @brief 销毁窗口
|
||||
*/
|
||||
virtual void destroy() = 0;
|
||||
|
||||
/**
|
||||
* @brief 轮询事件
|
||||
*/
|
||||
virtual void poll() = 0;
|
||||
|
||||
/**
|
||||
* @brief 交换缓冲区
|
||||
*/
|
||||
virtual void swap() = 0;
|
||||
|
||||
/**
|
||||
* @brief 窗口是否应该关闭
|
||||
*/
|
||||
virtual bool shouldClose() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置窗口关闭标志
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置窗口标题
|
||||
*/
|
||||
virtual void setTitle(const std::string& title) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置窗口大小
|
||||
*/
|
||||
virtual void setSize(int w, int h) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置窗口位置
|
||||
*/
|
||||
virtual void setPos(int x, int y) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置全屏模式
|
||||
*/
|
||||
virtual void setFullscreen(bool fs) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置垂直同步
|
||||
*/
|
||||
virtual void setVSync(bool vsync) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置窗口可见性
|
||||
*/
|
||||
virtual void setVisible(bool visible) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取窗口宽度
|
||||
*/
|
||||
virtual int width() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取窗口高度
|
||||
*/
|
||||
virtual int height() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取窗口大小
|
||||
*/
|
||||
virtual Size size() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取窗口位置
|
||||
*/
|
||||
virtual Vec2 pos() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 是否全屏
|
||||
*/
|
||||
virtual bool fullscreen() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 是否启用垂直同步
|
||||
*/
|
||||
virtual bool vsync() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 窗口是否获得焦点
|
||||
*/
|
||||
virtual bool focused() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 窗口是否最小化
|
||||
*/
|
||||
virtual bool minimized() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取内容缩放X
|
||||
*/
|
||||
virtual float scaleX() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取内容缩放Y
|
||||
*/
|
||||
virtual float scaleY() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置光标形状
|
||||
*/
|
||||
virtual void setCursor(Cursor cursor) = 0;
|
||||
|
||||
/**
|
||||
* @brief 显示/隐藏光标
|
||||
*/
|
||||
virtual void showCursor(bool show) = 0;
|
||||
|
||||
/**
|
||||
* @brief 锁定/解锁光标
|
||||
*/
|
||||
virtual void lockCursor(bool lock) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取输入接口
|
||||
*/
|
||||
virtual IInput* input() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 窗口大小改变回调
|
||||
*/
|
||||
using ResizeCb = std::function<void(int, int)>;
|
||||
|
||||
/**
|
||||
* @brief 窗口关闭回调
|
||||
*/
|
||||
using CloseCb = std::function<void()>;
|
||||
|
||||
/**
|
||||
* @brief 窗口焦点改变回调
|
||||
*/
|
||||
using FocusCb = std::function<void(bool)>;
|
||||
|
||||
/**
|
||||
* @brief 设置大小改变回调
|
||||
*/
|
||||
virtual void onResize(ResizeCb cb) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置关闭回调
|
||||
*/
|
||||
virtual void onClose(CloseCb cb) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置焦点改变回调
|
||||
*/
|
||||
virtual void onFocus(FocusCb cb) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取原生窗口句柄
|
||||
*/
|
||||
virtual void* native() const = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 键盘按键码
|
||||
*/
|
||||
enum class Key : int {
|
||||
None = 0,
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M,
|
||||
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||
Num0, Num1, Num2, Num3, Num4,
|
||||
Num5, Num6, Num7, Num8, Num9,
|
||||
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
|
||||
Space, Enter, Escape, Tab, Backspace,
|
||||
Insert, Delete, Home, End, PageUp, PageDown,
|
||||
Up, Down, Left, Right,
|
||||
LShift, RShift, LCtrl, RCtrl, LAlt, RAlt,
|
||||
CapsLock, NumLock, ScrollLock,
|
||||
Count
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 鼠标按钮
|
||||
*/
|
||||
enum class Mouse : int {
|
||||
Left = 0,
|
||||
Right,
|
||||
Middle,
|
||||
X1,
|
||||
X2,
|
||||
Count
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 游戏手柄按钮
|
||||
*/
|
||||
enum class Gamepad : int {
|
||||
A = 0,
|
||||
B,
|
||||
X,
|
||||
Y,
|
||||
LB,
|
||||
RB,
|
||||
LT,
|
||||
RT,
|
||||
Back,
|
||||
Start,
|
||||
Guide,
|
||||
LStick,
|
||||
RStick,
|
||||
DUp,
|
||||
DDown,
|
||||
DLeft,
|
||||
DRight,
|
||||
Count
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 手柄轴
|
||||
*/
|
||||
enum class GamepadAxis : int {
|
||||
LeftX = 0,
|
||||
LeftY,
|
||||
RightX,
|
||||
RightY,
|
||||
LeftTrigger,
|
||||
RightTrigger,
|
||||
Count
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/string.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <functional>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class EventQueue;
|
||||
class Input;
|
||||
|
||||
// ============================================================================
|
||||
// 窗口配置
|
||||
// ============================================================================
|
||||
struct WindowConfig {
|
||||
std::string title = "Extra2D Application";
|
||||
int width = 1280;
|
||||
int height = 720;
|
||||
bool fullscreen = true;
|
||||
bool resizable = false;
|
||||
bool vsync = true;
|
||||
int msaaSamples = 0;
|
||||
bool centerWindow = true;
|
||||
bool enableCursors = true;
|
||||
bool enableDpiScale = true;
|
||||
bool fullscreenDesktop = true; // true: SDL_WINDOW_FULLSCREEN_DESKTOP, false: SDL_WINDOW_FULLSCREEN
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 鼠标光标形状枚举
|
||||
// ============================================================================
|
||||
enum class CursorShape {
|
||||
Arrow,
|
||||
IBeam,
|
||||
Crosshair,
|
||||
Hand,
|
||||
HResize,
|
||||
VResize,
|
||||
ResizeAll,
|
||||
ResizeNWSE,
|
||||
ResizeNESW
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Window 类 - SDL2 Window + GLES 3.2 封装
|
||||
// 支持平台: Nintendo Switch, Windows, Linux, macOS
|
||||
// ============================================================================
|
||||
class Window {
|
||||
public:
|
||||
Window();
|
||||
~Window();
|
||||
|
||||
// 创建窗口
|
||||
bool create(const WindowConfig& config);
|
||||
void destroy();
|
||||
|
||||
// 窗口操作
|
||||
void pollEvents();
|
||||
void swapBuffers();
|
||||
bool shouldClose() const;
|
||||
void setShouldClose(bool close);
|
||||
|
||||
// 窗口属性
|
||||
void setTitle(const std::string& title);
|
||||
void setSize(int width, int height);
|
||||
void setPosition(int x, int y);
|
||||
void setFullscreen(bool fullscreen);
|
||||
void setVSync(bool enabled);
|
||||
void setResizable(bool resizable);
|
||||
|
||||
// 获取窗口属性
|
||||
int getWidth() const { return width_; }
|
||||
int getHeight() const { return height_; }
|
||||
Size getSize() const { return Size(static_cast<float>(width_), static_cast<float>(height_)); }
|
||||
Vec2 getPosition() const;
|
||||
bool isFullscreen() const { return fullscreen_; }
|
||||
bool isVSync() const { return vsync_; }
|
||||
|
||||
// DPI 缩放 (PC 端自动检测,Switch 固定 1.0)
|
||||
float getContentScaleX() const;
|
||||
float getContentScaleY() const;
|
||||
Vec2 getContentScale() const;
|
||||
|
||||
// 窗口状态
|
||||
bool isFocused() const { return focused_; }
|
||||
bool isMinimized() const;
|
||||
bool isMaximized() const;
|
||||
|
||||
// 获取 SDL2 窗口和 GL 上下文
|
||||
SDL_Window* getSDLWindow() const { return sdlWindow_; }
|
||||
SDL_GLContext getGLContext() const { return glContext_; }
|
||||
|
||||
// 设置/获取用户数据
|
||||
void setUserData(void* data) { userData_ = data; }
|
||||
void* getUserData() const { return userData_; }
|
||||
|
||||
// 事件队列
|
||||
void setEventQueue(EventQueue* queue) { eventQueue_ = queue; }
|
||||
EventQueue* getEventQueue() const { return eventQueue_; }
|
||||
|
||||
// 获取输入管理器
|
||||
Input* getInput() const { return input_.get(); }
|
||||
|
||||
// 光标操作 (PC 端有效,Switch 上为空操作)
|
||||
void setCursor(CursorShape shape);
|
||||
void resetCursor();
|
||||
void setMouseVisible(bool visible);
|
||||
|
||||
// 窗口回调
|
||||
using ResizeCallback = std::function<void(int width, int height)>;
|
||||
using FocusCallback = std::function<void(bool focused)>;
|
||||
using CloseCallback = std::function<void()>;
|
||||
|
||||
void setResizeCallback(ResizeCallback callback) { resizeCallback_ = callback; }
|
||||
void setFocusCallback(FocusCallback callback) { focusCallback_ = callback; }
|
||||
void setCloseCallback(CloseCallback callback) { closeCallback_ = callback; }
|
||||
|
||||
private:
|
||||
// SDL2 状态
|
||||
SDL_Window* sdlWindow_;
|
||||
SDL_GLContext glContext_;
|
||||
SDL_Cursor* sdlCursors_[9]; // 光标缓存
|
||||
SDL_Cursor* currentCursor_;
|
||||
|
||||
int width_;
|
||||
int height_;
|
||||
bool vsync_;
|
||||
bool shouldClose_;
|
||||
bool fullscreen_;
|
||||
bool focused_;
|
||||
float contentScaleX_;
|
||||
float contentScaleY_;
|
||||
bool enableDpiScale_;
|
||||
void* userData_;
|
||||
EventQueue* eventQueue_;
|
||||
UniquePtr<Input> input_;
|
||||
|
||||
ResizeCallback resizeCallback_;
|
||||
FocusCallback focusCallback_;
|
||||
CloseCallback closeCallback_;
|
||||
|
||||
bool initSDL(const WindowConfig& config);
|
||||
void deinitSDL();
|
||||
void initCursors();
|
||||
void deinitCursors();
|
||||
void updateContentScale();
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @file window_config.h
|
||||
* @brief 窗口模块配置
|
||||
*
|
||||
* 定义窗口相关的配置数据结构,由 WindowModule 管理。
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 窗口模式枚举
|
||||
*/
|
||||
enum class WindowMode {
|
||||
Windowed,
|
||||
Fullscreen,
|
||||
Borderless
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 窗口配置数据结构
|
||||
*/
|
||||
struct WindowConfigData {
|
||||
std::string title = "Extra2D Application";
|
||||
int width = 1280;
|
||||
int height = 720;
|
||||
int minWidth = 320;
|
||||
int minHeight = 240;
|
||||
int maxWidth = 0;
|
||||
int maxHeight = 0;
|
||||
WindowMode mode = WindowMode::Windowed;
|
||||
bool resizable = true;
|
||||
bool borderless = false;
|
||||
bool alwaysOnTop = false;
|
||||
bool centered = true;
|
||||
int posX = -1;
|
||||
int posY = -1;
|
||||
bool hideOnClose = false;
|
||||
bool minimizeOnClose = true;
|
||||
float opacity = 1.0f;
|
||||
bool transparentFramebuffer = false;
|
||||
bool highDPI = true;
|
||||
float contentScale = 1.0f;
|
||||
bool vsync = true;
|
||||
int multisamples = 0;
|
||||
bool visible = true;
|
||||
bool decorated = true;
|
||||
|
||||
/**
|
||||
* @brief 检查窗口尺寸是否有效
|
||||
* @return 如果宽高都大于0返回 true
|
||||
*/
|
||||
bool isSizeValid() const { return width > 0 && height > 0; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否设置了窗口位置
|
||||
* @return 如果设置了有效位置返回 true
|
||||
*/
|
||||
bool hasPosition() const { return posX >= 0 && posY >= 0; }
|
||||
|
||||
/**
|
||||
* @brief 获取窗口宽高比
|
||||
* @return 宽高比值
|
||||
*/
|
||||
float aspectRatio() const { return static_cast<float>(width) / static_cast<float>(height); }
|
||||
|
||||
/**
|
||||
* @brief 检查是否为全屏模式
|
||||
* @return 如果是全屏模式返回 true
|
||||
*/
|
||||
bool isFullscreen() const { return mode == WindowMode::Fullscreen; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否为无边框模式
|
||||
* @return 如果是无边框模式返回 true
|
||||
*/
|
||||
bool isBorderless() const { return mode == WindowMode::Borderless || borderless; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <extra2d/platform/window_config.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 窗口模块配置结构
|
||||
*/
|
||||
struct WindowCfg {
|
||||
std::string title;
|
||||
int w;
|
||||
int h;
|
||||
WindowMode mode;
|
||||
bool vsync;
|
||||
int priority;
|
||||
std::string backend;
|
||||
|
||||
WindowCfg()
|
||||
: title("Extra2D"), w(1280), h(720), mode(WindowMode::Windowed),
|
||||
vsync(true), priority(0), backend("sdl2") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 窗口模块
|
||||
* 管理窗口创建和生命周期
|
||||
*/
|
||||
class WindowModule : public Module {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数(Lambda 配置)
|
||||
* @param configFn 配置函数
|
||||
*/
|
||||
explicit WindowModule(std::function<void(WindowCfg &)> configFn);
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~WindowModule() override;
|
||||
|
||||
bool init() override;
|
||||
void shutdown() override;
|
||||
bool ok() const override { return initialized_; }
|
||||
const char *name() const override { return "window"; }
|
||||
int priority() const override { return cfg_.priority; }
|
||||
|
||||
/**
|
||||
* @brief 获取窗口
|
||||
* @return 窗口指针
|
||||
*/
|
||||
IWindow *win() const { return win_.get(); }
|
||||
|
||||
private:
|
||||
WindowCfg cfg_;
|
||||
UniquePtr<IWindow> win_;
|
||||
bool initialized_ = false;
|
||||
bool sdlInited_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,356 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/audio/sound.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/alpha_mask.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 资源管理器 - 统一管理纹理、字体、音效等资源
|
||||
// 支持异步加载和纹理压缩
|
||||
// ============================================================================
|
||||
|
||||
// 纹理格式枚举
|
||||
enum class TextureFormat {
|
||||
Auto = 0, // 自动选择最佳格式
|
||||
RGBA8, // 32位 RGBA
|
||||
RGB8, // 24位 RGB
|
||||
DXT1, // BC1/DXT1 压缩(1 bit alpha)
|
||||
DXT5, // BC3/DXT5 压缩(完整 alpha)
|
||||
ETC2, // ETC2 压缩(移动平台)
|
||||
ASTC4x4, // ASTC 4x4 压缩(高质量)
|
||||
ASTC8x8, // ASTC 8x8 压缩(高压缩率)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 纹理LRU缓存项
|
||||
// ============================================================================
|
||||
struct TextureCacheEntry {
|
||||
Ptr<Texture> texture;
|
||||
size_t size = 0; // 纹理大小(字节)
|
||||
float lastAccessTime = 0.0f; // 最后访问时间
|
||||
uint32_t accessCount = 0; // 访问次数
|
||||
};
|
||||
|
||||
// 异步加载回调类型
|
||||
using TextureLoadCallback = std::function<void(Ptr<Texture>)>;
|
||||
|
||||
class ResourceManager {
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
// 单例访问
|
||||
// ------------------------------------------------------------------------
|
||||
static ResourceManager &getInstance();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 纹理资源 - 同步加载
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 加载纹理(带缓存)
|
||||
Ptr<Texture> loadTexture(const std::string &filepath);
|
||||
|
||||
/// 加载纹理(指定是否异步)
|
||||
Ptr<Texture> loadTexture(const std::string &filepath, bool async);
|
||||
|
||||
/// 加载纹理(完整参数:异步 + 压缩格式)
|
||||
Ptr<Texture> loadTexture(const std::string &filepath, bool async, TextureFormat format);
|
||||
|
||||
/// 异步加载纹理(带回调)
|
||||
void loadTextureAsync(const std::string &filepath, TextureLoadCallback callback);
|
||||
|
||||
/// 异步加载纹理(指定格式 + 回调)
|
||||
void loadTextureAsync(const std::string &filepath, TextureFormat format, TextureLoadCallback callback);
|
||||
|
||||
/// 加载纹理并生成Alpha遮罩(用于不规则形状图片)
|
||||
Ptr<Texture> loadTextureWithAlphaMask(const std::string &filepath);
|
||||
|
||||
/// 通过key获取已缓存的纹理
|
||||
Ptr<Texture> getTexture(const std::string &key) const;
|
||||
|
||||
/// 检查纹理是否已缓存
|
||||
bool hasTexture(const std::string &key) const;
|
||||
|
||||
/// 卸载指定纹理
|
||||
void unloadTexture(const std::string &key);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Alpha遮罩资源
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 获取纹理的Alpha遮罩(如果已生成)
|
||||
const AlphaMask *getAlphaMask(const std::string &textureKey) const;
|
||||
|
||||
/// 为已加载的纹理生成Alpha遮罩
|
||||
bool generateAlphaMask(const std::string &textureKey);
|
||||
|
||||
/// 检查纹理是否有Alpha遮罩
|
||||
bool hasAlphaMask(const std::string &textureKey) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体图集资源
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 加载字体图集(带缓存)
|
||||
Ptr<FontAtlas> loadFont(const std::string &filepath, int fontSize,
|
||||
bool useSDF = false);
|
||||
|
||||
/// 通过key获取已缓存的字体图集
|
||||
Ptr<FontAtlas> getFont(const std::string &key) const;
|
||||
|
||||
/// 检查字体是否已缓存
|
||||
bool hasFont(const std::string &key) const;
|
||||
|
||||
/// 卸载指定字体
|
||||
void unloadFont(const std::string &key);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 音效资源
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 加载音效(带缓存)
|
||||
Ptr<Sound> loadSound(const std::string &filepath);
|
||||
Ptr<Sound> loadSound(const std::string &name, const std::string &filepath);
|
||||
|
||||
/// 通过key获取已缓存的音效
|
||||
Ptr<Sound> getSound(const std::string &key) const;
|
||||
|
||||
/// 检查音效是否已缓存
|
||||
bool hasSound(const std::string &key) const;
|
||||
|
||||
/// 卸载指定音效
|
||||
void unloadSound(const std::string &key);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文本文件资源
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 加载文本文件(带缓存)
|
||||
/// @param filepath 文件路径,支持 romfs:/ 前缀
|
||||
/// @return 文件内容字符串,加载失败返回空字符串
|
||||
std::string loadTextFile(const std::string &filepath);
|
||||
|
||||
/// 加载文本文件(指定编码)
|
||||
/// @param filepath 文件路径
|
||||
/// @param encoding 文件编码(默认 UTF-8)
|
||||
/// @return 文件内容字符串
|
||||
std::string loadTextFile(const std::string &filepath, const std::string &encoding);
|
||||
|
||||
/// 通过key获取已缓存的文本内容
|
||||
std::string getTextFile(const std::string &key) const;
|
||||
|
||||
/// 检查文本文件是否已缓存
|
||||
bool hasTextFile(const std::string &key) const;
|
||||
|
||||
/// 卸载指定文本文件
|
||||
void unloadTextFile(const std::string &key);
|
||||
|
||||
/// 清理所有文本文件缓存
|
||||
void clearTextFileCache();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// JSON 文件资源
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 加载并解析 JSON 文件
|
||||
/// @param filepath 文件路径,支持 romfs:/ 前缀
|
||||
/// @return JSON 字符串内容,加载或解析失败返回空字符串
|
||||
/// @note 返回的是原始 JSON 字符串,需要自行解析
|
||||
std::string loadJsonFile(const std::string &filepath);
|
||||
|
||||
/// 通过key获取已缓存的 JSON 内容
|
||||
std::string getJsonFile(const std::string &key) const;
|
||||
|
||||
/// 检查 JSON 文件是否已缓存
|
||||
bool hasJsonFile(const std::string &key) const;
|
||||
|
||||
/// 卸载指定 JSON 文件
|
||||
void unloadJsonFile(const std::string &key);
|
||||
|
||||
/// 清理所有 JSON 文件缓存
|
||||
void clearJsonFileCache();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 缓存清理
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 清理所有失效的弱引用(自动清理已释放的资源)
|
||||
void purgeUnused();
|
||||
|
||||
/// 清理指定类型的所有缓存
|
||||
void clearTextureCache();
|
||||
void clearFontCache();
|
||||
void clearSoundCache();
|
||||
|
||||
/// 清理所有资源缓存
|
||||
void clearAllCaches();
|
||||
|
||||
/// 获取各类资源的缓存数量
|
||||
size_t getTextureCacheSize() const;
|
||||
size_t getFontCacheSize() const;
|
||||
size_t getSoundCacheSize() const;
|
||||
size_t getTextFileCacheSize() const;
|
||||
size_t getJsonFileCacheSize() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// LRU 缓存管理
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 设置纹理缓存参数
|
||||
void setTextureCache(size_t maxCacheSize, size_t maxTextureCount,
|
||||
float unloadInterval);
|
||||
|
||||
/// 获取当前缓存的总大小(字节)
|
||||
size_t getTextureCacheMemoryUsage() const;
|
||||
|
||||
/// 获取缓存命中率
|
||||
float getTextureCacheHitRate() const;
|
||||
|
||||
/// 打印缓存统计信息
|
||||
void printTextureCacheStats() const;
|
||||
|
||||
/// 更新缓存(在主循环中调用,用于自动清理)
|
||||
void update(float dt);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 异步加载控制
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 初始化异步加载系统(可选,自动在首次异步加载时初始化)
|
||||
void initAsyncLoader();
|
||||
|
||||
/// 关闭异步加载系统
|
||||
void shutdownAsyncLoader();
|
||||
|
||||
/// 等待所有异步加载完成
|
||||
void waitForAsyncLoads();
|
||||
|
||||
/// 检查是否有正在进行的异步加载
|
||||
bool hasPendingAsyncLoads() const;
|
||||
|
||||
ResourceManager();
|
||||
~ResourceManager();
|
||||
ResourceManager(const ResourceManager &) = delete;
|
||||
ResourceManager &operator=(const ResourceManager &) = delete;
|
||||
|
||||
private:
|
||||
// 生成字体缓存key
|
||||
std::string makeFontKey(const std::string &filepath, int fontSize,
|
||||
bool useSDF) const;
|
||||
|
||||
// 内部加载实现
|
||||
Ptr<Texture> loadTextureInternal(const std::string &filepath, TextureFormat format);
|
||||
|
||||
// 选择最佳纹理格式
|
||||
TextureFormat selectBestFormat(TextureFormat requested) const;
|
||||
|
||||
// 压缩纹理数据
|
||||
std::vector<uint8_t> compressTexture(const uint8_t* data, int width, int height,
|
||||
int channels, TextureFormat format);
|
||||
|
||||
// 互斥锁保护缓存
|
||||
mutable std::mutex textureMutex_;
|
||||
mutable std::mutex fontMutex_;
|
||||
mutable std::mutex soundMutex_;
|
||||
mutable std::mutex textFileMutex_;
|
||||
mutable std::mutex jsonFileMutex_;
|
||||
|
||||
// 资源缓存 - 使用弱指针实现自动清理
|
||||
std::unordered_map<std::string, WeakPtr<FontAtlas>> fontCache_;
|
||||
std::unordered_map<std::string, WeakPtr<Sound>> soundCache_;
|
||||
|
||||
// 文本文件缓存 - 使用强引用(字符串值类型)
|
||||
std::unordered_map<std::string, std::string> textFileCache_;
|
||||
std::unordered_map<std::string, std::string> jsonFileCache_;
|
||||
|
||||
// ============================================================================
|
||||
// 纹理LRU缓存
|
||||
// ============================================================================
|
||||
|
||||
// LRU链表节点
|
||||
struct LRUNode {
|
||||
std::string key;
|
||||
uint32_t prev = 0; // 数组索引,0表示无效
|
||||
uint32_t next = 0; // 数组索引,0表示无效
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
// 纹理缓存配置
|
||||
size_t maxCacheSize_ = 64 * 1024 * 1024; // 最大缓存大小 (64MB)
|
||||
size_t maxTextureCount_ = 256; // 最大纹理数量
|
||||
float unloadInterval_ = 30.0f; // 自动清理间隔 (秒)
|
||||
|
||||
// 纹理缓存 - 使用强指针保持引用
|
||||
std::unordered_map<std::string, TextureCacheEntry> textureCache_;
|
||||
|
||||
// 侵入式LRU链表 - 使用数组索引代替指针,提高缓存局部性
|
||||
std::vector<LRUNode> lruNodes_;
|
||||
uint32_t lruHead_ = 0; // 最近使用
|
||||
uint32_t lruTail_ = 0; // 最久未使用
|
||||
uint32_t freeList_ = 0; // 空闲节点链表
|
||||
|
||||
// 统计
|
||||
size_t totalTextureSize_ = 0;
|
||||
uint64_t textureHitCount_ = 0;
|
||||
uint64_t textureMissCount_ = 0;
|
||||
float autoUnloadTimer_ = 0.0f;
|
||||
|
||||
// 异步加载相关
|
||||
struct AsyncLoadTask {
|
||||
std::string filepath;
|
||||
TextureFormat format;
|
||||
TextureLoadCallback callback;
|
||||
std::promise<Ptr<Texture>> promise;
|
||||
};
|
||||
|
||||
std::queue<AsyncLoadTask> asyncTaskQueue_;
|
||||
std::mutex asyncQueueMutex_;
|
||||
std::condition_variable asyncCondition_;
|
||||
std::unique_ptr<std::thread> asyncThread_;
|
||||
std::atomic<bool> asyncRunning_{false};
|
||||
std::atomic<int> pendingAsyncLoads_{0};
|
||||
|
||||
void asyncLoadLoop();
|
||||
|
||||
// ============================================================================
|
||||
// LRU 缓存内部方法
|
||||
// ============================================================================
|
||||
|
||||
/// 分配LRU节点
|
||||
uint32_t allocateLRUNode(const std::string &key);
|
||||
|
||||
/// 释放LRU节点
|
||||
void freeLRUNode(uint32_t index);
|
||||
|
||||
/// 将节点移到链表头部(最近使用)
|
||||
void moveToFront(uint32_t index);
|
||||
|
||||
/// 从链表中移除节点
|
||||
void removeFromList(uint32_t index);
|
||||
|
||||
/// 驱逐最久未使用的纹理
|
||||
std::string evictLRU();
|
||||
|
||||
/// 访问纹理(更新LRU位置)
|
||||
void touchTexture(const std::string &key);
|
||||
|
||||
/// 驱逐纹理直到满足大小限制
|
||||
void evictTexturesIfNeeded();
|
||||
|
||||
/// 计算纹理大小
|
||||
size_t calculateTextureSize(int width, int height, PixelFormat format) const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/event/event_dispatcher.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <functional>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -14,7 +13,6 @@ namespace extra2d {
|
|||
|
||||
// 前向声明
|
||||
class Scene;
|
||||
class Action;
|
||||
class RenderBackend;
|
||||
struct RenderCommand;
|
||||
|
||||
|
|
@ -39,19 +37,19 @@ public:
|
|||
|
||||
void removeChild(Ptr<Node> child);
|
||||
void removeChildByName(const std::string &name);
|
||||
void removeFromParent();
|
||||
void removeAllChildren();
|
||||
void detach();
|
||||
void clearChildren();
|
||||
|
||||
Ptr<Node> getParent() const { return parent_.lock(); }
|
||||
const std::vector<Ptr<Node>> &getChildren() const { return children_; }
|
||||
Ptr<Node> getChildByName(const std::string &name) const;
|
||||
Ptr<Node> getChildByTag(int tag) const;
|
||||
Ptr<Node> findChild(const std::string &name) const;
|
||||
Ptr<Node> findChildByTag(int tag) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 变换属性
|
||||
// ------------------------------------------------------------------------
|
||||
void setPosition(const Vec2 &pos);
|
||||
void setPosition(float x, float y);
|
||||
void setPos(const Vec2 &pos);
|
||||
void setPos(float x, float y);
|
||||
Vec2 getPosition() const { return position_; }
|
||||
|
||||
void setRotation(float degrees);
|
||||
|
|
@ -80,7 +78,7 @@ public:
|
|||
* @brief 设置颜色
|
||||
* @param color RGB颜色
|
||||
*/
|
||||
void setColor(const Color3B& color);
|
||||
void setColor(const Color3B &color);
|
||||
Color3B getColor() const { return color_; }
|
||||
|
||||
/**
|
||||
|
|
@ -101,8 +99,8 @@ public:
|
|||
// ------------------------------------------------------------------------
|
||||
// 世界变换
|
||||
// ------------------------------------------------------------------------
|
||||
Vec2 convertToWorldSpace(const Vec2 &localPos) const;
|
||||
Vec2 convertToNodeSpace(const Vec2 &worldPos) const;
|
||||
Vec2 toWorld(const Vec2 &localPos) const;
|
||||
Vec2 toLocal(const Vec2 &worldPos) const;
|
||||
|
||||
glm::mat4 getLocalTransform() const;
|
||||
glm::mat4 getWorldTransform() const;
|
||||
|
|
@ -116,7 +114,7 @@ public:
|
|||
* @brief 批量更新变换矩阵
|
||||
* 在渲染前统一计算所有脏节点的变换矩阵,避免逐节点计算时的重复递归
|
||||
*/
|
||||
void batchUpdateTransforms();
|
||||
void batchTransforms();
|
||||
|
||||
/**
|
||||
* @brief 获取变换脏标记状态
|
||||
|
|
@ -144,68 +142,9 @@ public:
|
|||
virtual void onDetachFromScene();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 边界框(用于空间索引)
|
||||
// 边界框
|
||||
// ------------------------------------------------------------------------
|
||||
virtual Rect getBoundingBox() const;
|
||||
|
||||
// 是否需要参与空间索引(默认 true)
|
||||
void setSpatialIndexed(bool indexed) { spatialIndexed_ = indexed; }
|
||||
bool isSpatialIndexed() const { return spatialIndexed_; }
|
||||
|
||||
// 更新空间索引(手动调用,通常在边界框变化后)
|
||||
void updateSpatialIndex();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 动作系统
|
||||
// ------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief 运行动作
|
||||
* @param action 动作指针(所有权转移)
|
||||
* @return 动作指针
|
||||
*/
|
||||
Action* runAction(Action* action);
|
||||
|
||||
/**
|
||||
* @brief 停止所有动作
|
||||
*/
|
||||
void stopAllActions();
|
||||
|
||||
/**
|
||||
* @brief 停止指定动作
|
||||
* @param action 动作指针
|
||||
*/
|
||||
void stopAction(Action* action);
|
||||
|
||||
/**
|
||||
* @brief 根据标签停止动作
|
||||
* @param tag 标签值
|
||||
*/
|
||||
void stopActionByTag(int tag);
|
||||
|
||||
/**
|
||||
* @brief 根据标志位停止动作
|
||||
* @param flags 标志位
|
||||
*/
|
||||
void stopActionsByFlags(unsigned int flags);
|
||||
|
||||
/**
|
||||
* @brief 根据标签获取动作
|
||||
* @param tag 标签值
|
||||
* @return 动作指针,未找到返回 nullptr
|
||||
*/
|
||||
Action* getActionByTag(int tag);
|
||||
|
||||
/**
|
||||
* @brief 获取运行中的动作数量
|
||||
* @return 动作数量
|
||||
*/
|
||||
size_t getActionCount() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否有动作在运行
|
||||
* @return true 如果有动作在运行
|
||||
*/
|
||||
bool isRunningActions() const;
|
||||
virtual Rect getBounds() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 事件系统
|
||||
|
|
@ -271,10 +210,7 @@ private:
|
|||
Vec2 anchor_ = Vec2(0.5f, 0.5f); // 8 bytes
|
||||
Vec2 skew_ = Vec2::Zero(); // 8 bytes
|
||||
|
||||
// 8. 边界框(用于空间索引)
|
||||
Rect lastSpatialBounds_; // 16 bytes
|
||||
|
||||
// 9. 浮点属性
|
||||
// 8. 浮点属性
|
||||
float rotation_ = 0.0f; // 4 bytes
|
||||
float opacity_ = 1.0f; // 4 bytes
|
||||
|
||||
|
|
@ -289,17 +225,15 @@ private:
|
|||
bool flipX_ = false; // 1 byte
|
||||
bool flipY_ = false; // 1 byte
|
||||
|
||||
// 11. 场景指针
|
||||
// 13. 场景指针
|
||||
Scene *scene_ = nullptr; // 8 bytes
|
||||
|
||||
// 12. 布尔标志(打包在一起)
|
||||
// 14. 布尔标志(打包在一起)
|
||||
mutable bool transformDirty_ = true; // 1 byte
|
||||
mutable bool worldTransformDirty_ = true; // 1 byte
|
||||
bool childrenOrderDirty_ = false; // 1 byte
|
||||
bool visible_ = true; // 1 byte
|
||||
bool running_ = false; // 1 byte
|
||||
bool spatialIndexed_ = true; // 1 byte
|
||||
// 填充 2 bytes 到 8 字节对齐
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/graphics/camera.h>
|
||||
#include <extra2d/graphics/camera/camera.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <extra2d/spatial/spatial_manager.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -61,28 +60,6 @@ public:
|
|||
void collectRenderCommands(std::vector<RenderCommand> &commands,
|
||||
int parentZOrder = 0) override;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 空间索引系统
|
||||
// ------------------------------------------------------------------------
|
||||
SpatialManager &getSpatialManager() { return spatialManager_; }
|
||||
const SpatialManager &getSpatialManager() const { return spatialManager_; }
|
||||
|
||||
// 启用/禁用空间索引
|
||||
void setSpatialIndexingEnabled(bool enabled) {
|
||||
spatialIndexingEnabled_ = enabled;
|
||||
}
|
||||
bool isSpatialIndexingEnabled() const { return spatialIndexingEnabled_; }
|
||||
|
||||
// 节点空间索引管理(内部使用)
|
||||
void updateNodeInSpatialIndex(Node *node, const Rect &oldBounds,
|
||||
const Rect &newBounds);
|
||||
void removeNodeFromSpatialIndex(Node *node);
|
||||
|
||||
// 碰撞检测查询
|
||||
std::vector<Node *> queryNodesInArea(const Rect &area) const;
|
||||
std::vector<Node *> queryNodesAtPoint(const Vec2 &point) const;
|
||||
std::vector<std::pair<Node *, Node *>> queryCollisions() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -107,10 +84,6 @@ private:
|
|||
Ptr<Camera> defaultCamera_;
|
||||
|
||||
bool paused_ = false;
|
||||
|
||||
// 空间索引系统
|
||||
SpatialManager spatialManager_;
|
||||
bool spatialIndexingEnabled_ = true;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/scene/scene.h>
|
||||
#include <extra2d/scene/transition_scene.h>
|
||||
|
||||
#include <functional>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
|
@ -11,65 +11,89 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
struct RenderCommand;
|
||||
class TransitionScene;
|
||||
enum class TransitionType;
|
||||
|
||||
// ============================================================================
|
||||
// 场景管理器 - 管理场景的生命周期和切换
|
||||
// ============================================================================
|
||||
/**
|
||||
* @brief 场景管理器 - 管理场景的生命周期和切换
|
||||
*/
|
||||
class SceneManager {
|
||||
public:
|
||||
using TransitionCallback = std::function<void()>;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 单例访问
|
||||
// ------------------------------------------------------------------------
|
||||
static SceneManager &getInstance();
|
||||
static SceneManager &get();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 场景栈操作
|
||||
// 基本场景操作(无过渡效果)
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// 运行第一个场景
|
||||
void runWithScene(Ptr<Scene> scene);
|
||||
|
||||
// 替换当前场景
|
||||
void replaceScene(Ptr<Scene> scene);
|
||||
void pushScene(Ptr<Scene> scene);
|
||||
void popScene();
|
||||
void popToRootScene();
|
||||
void popToScene(const std::string &name);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 带过渡效果的场景操作
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 替换当前场景(带过渡效果)
|
||||
* @param scene 新场景
|
||||
* @param transition 过渡类型
|
||||
* @param duration 过渡持续时间(秒)
|
||||
*/
|
||||
void replaceScene(Ptr<Scene> scene, TransitionType transition,
|
||||
float duration = 0.5f);
|
||||
|
||||
// 压入新场景(当前场景暂停)
|
||||
void pushScene(Ptr<Scene> scene);
|
||||
/**
|
||||
* @brief 压入新场景(带过渡效果)
|
||||
* @param scene 新场景
|
||||
* @param transition 过渡类型
|
||||
* @param duration 过渡持续时间(秒)
|
||||
*/
|
||||
void pushScene(Ptr<Scene> scene, TransitionType transition,
|
||||
float duration = 0.5f);
|
||||
|
||||
// 弹出当前场景(恢复上一个场景)
|
||||
void popScene();
|
||||
/**
|
||||
* @brief 弹出当前场景(带过渡效果)
|
||||
* @param transition 过渡类型
|
||||
* @param duration 过渡持续时间(秒)
|
||||
*/
|
||||
void popScene(TransitionType transition, float duration = 0.5f);
|
||||
|
||||
// 弹出到根场景
|
||||
void popToRootScene();
|
||||
/**
|
||||
* @brief 弹出到根场景(带过渡效果)
|
||||
* @param transition 过渡类型
|
||||
* @param duration 过渡持续时间(秒)
|
||||
*/
|
||||
void popToRootScene(TransitionType transition, float duration = 0.5f);
|
||||
|
||||
// 弹出到指定场景
|
||||
void popToScene(const std::string &name);
|
||||
/**
|
||||
* @brief 弹出到指定场景(带过渡效果)
|
||||
* @param name 目标场景名称
|
||||
* @param transition 过渡类型
|
||||
* @param duration 过渡持续时间(秒)
|
||||
*/
|
||||
void popToScene(const std::string &name, TransitionType transition,
|
||||
float duration = 0.5f);
|
||||
|
||||
/**
|
||||
* @brief 使用自定义过渡场景进入场景
|
||||
* @param scene 新场景
|
||||
* @param transitionScene 过渡场景
|
||||
*/
|
||||
void enterScene(Ptr<Scene> scene, Ptr<TransitionScene> transitionScene);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 获取场景
|
||||
// 场景查询
|
||||
// ------------------------------------------------------------------------
|
||||
Ptr<Scene> getCurrentScene() const;
|
||||
Ptr<Scene> getPreviousScene() const;
|
||||
Ptr<Scene> getRootScene() const;
|
||||
|
||||
// 通过名称获取场景
|
||||
Ptr<Scene> getSceneByName(const std::string &name) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 查询
|
||||
// ------------------------------------------------------------------------
|
||||
size_t getSceneCount() const { return sceneStack_.size(); }
|
||||
bool isEmpty() const { return sceneStack_.empty(); }
|
||||
bool hasScene(const std::string &name) const;
|
||||
|
|
@ -82,16 +106,13 @@ public:
|
|||
void collectRenderCommands(std::vector<RenderCommand> &commands);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 过渡控制
|
||||
// 过渡状态
|
||||
// ------------------------------------------------------------------------
|
||||
bool isTransitioning() const { return isTransitioning_; }
|
||||
void setTransitionCallback(TransitionCallback callback) {
|
||||
transitionCallback_ = callback;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 清理
|
||||
// ------------------------------------------------------------------------
|
||||
void end();
|
||||
void purgeCachedScenes();
|
||||
|
||||
|
|
@ -101,36 +122,50 @@ public:
|
|||
SceneManager(const SceneManager &) = delete;
|
||||
SceneManager &operator=(const SceneManager &) = delete;
|
||||
|
||||
// 场景切换(供 Application 使用)
|
||||
void enterScene(Ptr<Scene> scene);
|
||||
void enterScene(Ptr<Scene> scene, Ptr<TransitionScene> transitionScene);
|
||||
|
||||
private:
|
||||
void doSceneSwitch();
|
||||
/**
|
||||
* @brief 启动过渡
|
||||
* @param from 源场景
|
||||
* @param to 目标场景
|
||||
* @param type 过渡类型
|
||||
* @param duration 过渡持续时间
|
||||
* @param stackAction 过渡完成后的栈操作
|
||||
*/
|
||||
void startTransition(Ptr<Scene> from, Ptr<Scene> to, TransitionType type,
|
||||
float duration, Function<void()> stackAction);
|
||||
void finishTransition();
|
||||
void dispatchPointerEvents(Scene &scene);
|
||||
float duration, std::function<void()> stackAction);
|
||||
|
||||
// 创建过渡场景
|
||||
Ptr<TransitionScene> createTransitionScene(TransitionType type,
|
||||
float duration,
|
||||
/**
|
||||
* @brief 创建过渡场景
|
||||
* @param type 过渡类型
|
||||
* @param duration 过渡持续时间
|
||||
* @param inScene 目标场景
|
||||
* @return 过渡场景智能指针
|
||||
*/
|
||||
Ptr<TransitionScene> createTransitionScene(TransitionType type, float duration,
|
||||
Ptr<Scene> inScene);
|
||||
|
||||
/**
|
||||
* @brief 完成过渡
|
||||
*/
|
||||
void finishTransition();
|
||||
|
||||
void doSceneSwitch();
|
||||
void dispatchPointerEvents(Scene &scene);
|
||||
|
||||
std::stack<Ptr<Scene>> sceneStack_;
|
||||
std::unordered_map<std::string, Ptr<Scene>> namedScenes_;
|
||||
|
||||
// Transition state
|
||||
bool isTransitioning_ = false;
|
||||
TransitionType currentTransition_ = TransitionType::None;
|
||||
Ptr<TransitionScene> activeTransitionScene_;
|
||||
Function<void()> transitionStackAction_;
|
||||
TransitionCallback transitionCallback_;
|
||||
|
||||
// Next scene to switch to (queued during transition)
|
||||
Ptr<Scene> nextScene_;
|
||||
bool sendCleanupToScene_ = false;
|
||||
|
||||
Ptr<TransitionScene> activeTransitionScene_;
|
||||
std::function<void()> transitionStackAction_;
|
||||
|
||||
Node *hoverTarget_ = nullptr;
|
||||
Node *captureTarget_ = nullptr;
|
||||
Vec2 lastPointerWorld_ = Vec2::Zero();
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ public:
|
|||
void addPoint(const Vec2 &point);
|
||||
void clearPoints();
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
Rect getBounds() const override;
|
||||
|
||||
protected:
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -37,7 +37,7 @@ public:
|
|||
static Ptr<Sprite> create(Ptr<Texture> texture);
|
||||
static Ptr<Sprite> create(Ptr<Texture> texture, const Rect &rect);
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
Rect getBounds() const override;
|
||||
|
||||
protected:
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue