Compare commits

...

42 Commits
main ... master

Author SHA1 Message Date
ChestnutYueyue 583e866861 docs(module_system): 重构模块系统文档,简化内容并优化结构
- 移除冗余的配置系统实现细节,聚焦核心架构
- 优化模块与服务的对比表格,突出关键差异
- 简化示例代码,保留核心用法展示
- 更新架构图,更清晰展示模块与服务的层级关系
- 统一术语使用,增强文档一致性
2026-02-17 23:57:50 +08:00
ChestnutYueyue edd47a890a chore: 删除无用的图片文件1.jpg 2026-02-17 23:02:18 +08:00
ChestnutYueyue 69606230da docs: 更新架构图添加GLFW后端支持
在平台层架构图中添加GLFW后端支持,包括窗口系统和输入系统的关联
2026-02-17 22:57:46 +08:00
ChestnutYueyue 6babd376c9 docs: 更新 README 文档以反映最新架构变更
- 添加资源抽象层和显存管理功能说明
- 更新模块优先级和架构图
- 完善目录结构描述
- 新增性能优化策略说明
2026-02-17 22:53:41 +08:00
ChestnutYueyue 2748e80dea refactor(opengl): 重构渲染器资源管理并引入资源抽象层
- 引入资源抽象层接口(Buffer、Pipeline、Framebuffer等)
- 将OpenGL资源管理重构为GLBuffer、GLPipeline、GLFramebuffer等实现类
- 使用GLBuffer替代手动管理的VBO/IBO,提供更安全的资源生命周期管理
- 新增GLPipeline管理OpenGL管线状态,减少冗余状态切换
- 新增GLFramebuffer封装帧缓冲对象功能
- 更新GLRenderer使用新的资源管理方式
- 添加详细文档说明资源抽象层设计
- 修复相关内存泄漏问题

docs: 添加资源抽象层文档说明

- 新增docs/README.md详细说明资源抽象层设计
- 文档包含各接口功能说明和实现原则
2026-02-17 22:36:02 +08:00
ChestnutYueyue c8a6ea19e3 refactor(renderer): 优化文本渲染的批处理逻辑并调整代码格式
重构文本渲染的批处理逻辑,添加纹理变化检查并优化换行处理
同时调整部分代码格式以提高可读性
2026-02-17 20:44:33 +08:00
ChestnutYueyue 32e12b8c99 feat(渲染): 添加自动批处理功能并实现图片显示示例
添加自动精灵批处理功能,优化渲染性能
新增图片显示示例,展示如何使用RenderBackend抽象接口加载和显示图片
重构文本渲染示例以使用RenderBackend接口
添加flush方法用于手动控制批处理提交时机
2026-02-17 20:16:07 +08:00
ChestnutYueyue 6b4ce69657 feat(渲染): 优化字体图集和精灵批处理系统
- 在GLFontAtlas中重构字体图集实现,使用stb_rect_pack进行动态矩形打包
- 增加图集尺寸至1024x1024并添加字形间距
- 改进字形UV计算和纹理坐标处理
- 在GLSpriteBatch中保存viewProjection矩阵并优化着色器uniform设置
- 修正文本渲染中的字形位置计算和精灵中心点处理
- 优化纹理坐标生成逻辑以正确处理UV翻转
2026-02-17 19:55:47 +08:00
ChestnutYueyue 30b677f192 feat(示例): 添加文字渲染示例并修复场景过渡方法声明
添加新的文字渲染示例 demo_text_rendering,展示如何使用 GLFontAtlas 渲染文字
修复场景过渡类中缺少 override 关键字的方法声明
更新 README 文档以包含新示例
2026-02-17 19:24:50 +08:00
ChestnutYueyue 89fb955eb0 docs: 更新README以反映新增的Vulkan渲染后端和架构调整
更新文档以包含以下内容:
1. 新增Vulkan渲染后端支持
2. 渲染系统分层架构说明
3. 目录结构调整
4. 多平台支持状态更新
2026-02-17 14:50:13 +08:00
ChestnutYueyue f1cf6a6d85 docs: 更新README添加多后端渲染和高性能批处理特性 2026-02-17 14:50:05 +08:00
ChestnutYueyue a4276e4376 feat(渲染后端): 重构渲染系统支持多后端
- 新增渲染后端工厂类,支持OpenGL和Vulkan后端
- 将OpenGL相关代码移动到backends/opengl目录
- 添加Vulkan后端占位实现
- 重构Shader系统,支持JSON元数据定义多后端Shader
- 新增shape和sprite的JSON定义及GLSL文件
- 移除旧的组合Shader文件格式
- 更新构建系统支持选择渲染后端
- 重命名相关头文件路径保持一致性
2026-02-17 14:48:04 +08:00
ChestnutYueyue d2660a86bb refactor(config): 移除平台检测和配置相关代码
将应用配置信息移至Application类,移除平台检测和配置相关文件
简化ShaderManager的初始化逻辑,使用相对路径替代平台检测
2026-02-17 12:53:01 +08:00
ChestnutYueyue 61dea772f7 feat(服务定位器): 实现服务自动注册机制并重构服务初始化流程
添加服务自动注册宏E2D_AUTO_REGISTER_SERVICE,通过模板元编程实现编译期服务注册
重构Application初始化流程,移除手动服务注册代码,改为自动注册方式
统一各服务实现类的代码风格,优化缩进和格式
2026-02-17 12:12:22 +08:00
ChestnutYueyue 4f02ad0e39 refactor(模块系统): 重构模块配置方式为lambda函数
将InputModule、WindowModule、RenderModule等核心模块的配置方式从结构体改为lambda函数
添加独立的配置结构体(InputCfg/WindowCfg/RenderCfg)并更新文档
实现GLFW窗口居中功能
更新示例代码使用新的配置方式
2026-02-17 00:34:14 +08:00
ChestnutYueyue 4b1de5e36a refactor: 整理头文件顺序并格式化代码
重构头文件引入顺序以保持一致性,并统一代码格式化风格
2026-02-17 00:12:32 +08:00
ChestnutYueyue 9f83b8fde5 feat(platform): 添加 GLFW 后端支持并移除 SDL2 依赖
添加 GLFW 作为可选的窗口和输入后端,支持通过配置切换 SDL2 或 GLFW 后端
移除对 SDL2 的直接依赖,重构窗口模块以支持多后端
更新构建系统和文档以反映后端选择功能
2026-02-17 00:06:31 +08:00
ChestnutYueyue 0425425ec7 refactor: 重构 graphics 模块目录结构并简化配置系统
- 重组 graphics 目录,按功能分为 core/camera/shader/texture/memory 子目录
- 移除所有模块级 *_config.h 配置文件,改用模块内部的 Cfg 结构体
- 移除 config_loader 和 debug_config 相关文件
- 简化模块系统,使用 Module 基类替代复杂的 IModuleConfig/IModuleInitializer
- 添加 SDL_GetBasePath() 支持跨平台 shader 路径解析
- 修复日志宏不支持 {} 格式化语法的问题
- 更新文档反映新的目录结构
2026-02-16 23:14:12 +08:00
ChestnutYueyue 6273f3235d feat(scene): 添加多种场景过渡效果实现
实现缩放、方块、滑动、翻页和淡入淡出五种场景过渡效果
新增 TransitionScene 基类作为过渡场景的公共父类
扩展 SceneManager 支持带过渡效果的场景切换操作
2026-02-15 18:00:32 +08:00
ChestnutYueyue b55d279611 feat(示例): 添加自定义模块示例和文档
新增HelloModule示例展示如何创建自定义模块,包含:
1. 模块配置类实现
2. 模块初始化器实现
3. 自动注册机制
4. JSON配置支持
5. 场景中使用模块的示例

同时更新模块系统文档,详细说明自定义模块开发流程
2026-02-15 17:00:39 +08:00
ChestnutYueyue 867013f6eb refactor(shader): 合并GLShaderNew到GLShader并优化着色器缓存逻辑
- 删除GLShaderNew类,将其功能合并到GLShader中
- 增加着色器缓存保存时的空二进制检查
- 添加更多调试日志信息
- 优化二进制数据获取的错误处理
2026-02-15 16:24:35 +08:00
ChestnutYueyue 78680138c2 build: 根据平台环境配置SDL2的Wayland支持
在Linux平台下检测XDG_SESSION_TYPE环境变量,动态启用SDL2的Wayland后端支持。修改xmake.lua构建配置以适配不同显示服务器环境。
2026-02-15 15:35:26 +08:00
ChestnutYueyue 0700bad5d9 docs: 添加项目README文件
添加项目README文件,包含项目简介、核心特性、架构概览、构建指南、快速开始示例、技术栈、文档结构等信息
2026-02-15 14:03:16 +08:00
ChestnutYueyue 2d8b98bba6 docs(模块系统): 添加场景图和视口适配系统的文档
添加场景图系统的详细说明,包括Node基类、Scene类、ShapeNode和变换继承
添加视口适配系统的文档,包括ViewportAdapter和不同适配模式的说明
更新示例部分以包含新的场景相关实现文件
2026-02-15 13:39:18 +08:00
ChestnutYueyue 3216a190ce refactor(渲染): 将视图投影矩阵设置移至Application层
重构渲染流程,将视图投影矩阵的设置从Scene移动到Application层,通过CameraService统一管理。同时更新示例代码以适配此变更,确保视口配置正确应用。
2026-02-15 13:37:08 +08:00
ChestnutYueyue 6c6cac55f7 feat(渲染): 实现节点层级变换支持
refactor(场景图): 简化形状节点渲染逻辑

feat(输入): 添加输入事件到事件服务的连接

docs(模块系统): 更新文档说明模块配置和平台支持

test(示例): 添加场景图测试示例展示节点变换
2026-02-15 13:32:42 +08:00
ChestnutYueyue 8c56c29cd2 refactor(platform): 重构平台和模块配置系统
将平台相关配置分离到独立头文件
移除AppConfig中的模块配置,改为模块自行管理
统一使用SDL2作为窗口后端
优化Switch平台支持
添加模块配置接口
重构配置加载器以支持模块配置
2026-02-15 12:36:36 +08:00
ChestnutYueyue 453a057c7d feat: 实现服务定位器模式并重构核心服务架构
重构应用程序架构,引入服务定位器模式解耦模块依赖
新增服务注册表和服务定位器核心组件
实现核心服务接口及实现类(场景、计时器、事件、相机服务)
重构Application类使用服务定位器管理服务
添加服务自动注册宏和类型安全服务获取机制
2026-02-15 11:40:57 +08:00
ChestnutYueyue f8ecf7e03a feat(渲染): 添加内置着色器管理和安装功能
- 移除font.shader中u_smoothing的默认值
- 添加xmake.lua中的着色器安装函数,支持不同平台
- 在渲染模块初始化时初始化ShaderManager并加载内置着色器
- 在渲染模块关闭时清理ShaderManager资源
2026-02-15 11:22:13 +08:00
ChestnutYueyue 475ae50d2a refactor(shader): 重构着色器系统并添加新功能
- 将shader_system.h重命名为shader_manager.h并重构接口
- 新增shader_interface.h作为跨平台着色器抽象
- 实现GLShaderNew作为OpenGL着色器新实现
- 添加shader_cache支持着色器二进制缓存
- 引入shader_hot_reloader实现热重载功能
- 新增shader_loader支持多种着色器文件格式加载
- 添加内置着色器文件到shaders目录
- 更新gl_renderer.cpp使用新的着色器系统
- 扩展platform_detector.h添加资源路径相关方法
- 添加shaders/common目录包含常用GLSL工具函数

重构后的着色器系统提供更完善的缓存、热重载和跨平台支持,同时优化了性能和维护性。
2026-02-15 11:12:27 +08:00
ChestnutYueyue 269f2d907d docs: 添加模块系统文档
添加 Extra2D 模块系统的详细文档,包括架构设计、核心接口、创建模块步骤、内置模块说明和最佳实践
2026-02-15 10:11:09 +08:00
ChestnutYueyue 8f7f1612eb refactor(module): 重构模块系统并添加核心模块实现
重构模块注册表结构以支持初始化器实例缓存,添加配置、日志、平台和渲染等核心模块实现
优化日志系统移除SDL依赖,改进跨平台支持
调整模块初始化流程,增强模块间依赖管理
2026-02-15 10:08:44 +08:00
ChestnutYueyue 38148a6c54 feat: 添加SDL2后端支持并实现基础示例程序
添加SDL2作为平台后端支持,包括窗口创建、输入处理和GLAD初始化
实现基础示例程序展示引擎基本功能
重构平台配置代码以提高可读性
移除未使用的input_codes.h头文件
添加demo_basic构建目标到xmake配置
2026-02-15 09:22:57 +08:00
ChestnutYueyue 34fe0bafcb refactor(config): 重构配置系统并添加模块化架构
feat(config): 添加新的配置管理器、加载器和平台检测器
feat(module): 实现模块注册表和初始化系统
refactor(window): 将窗口配置迁移到新的配置系统
refactor(input): 重构输入系统以支持模块化
refactor(render): 更新渲染系统以适配新架构
refactor(graphics): 移除冗余API并简化渲染目标接口
refactor(app): 重构应用类以整合新的配置和模块系统
refactor(color): 移除废弃的ColorConstants结构
style: 清理代码并修复格式问题
2026-02-15 08:51:31 +08:00
ChestnutYueyue 9439e200d7 refactor(platform): 重构平台模块,实现多后端支持
重构平台模块,引入IWindow和IInput接口,支持SDL2和GLFW等多后端。主要变更包括:
1. 新增平台模块接口和SDL2后端实现
2. 移除旧版Window和Input类,替换为接口化设计
3. 添加后端注册机制,支持动态加载不同平台后端
4. 统一输入系统API,定义标准键盘、鼠标和手柄按键枚举
5. 更新构建系统,支持通过配置选择不同后端

同时调整相关代码以适配新接口,包括渲染器、场景管理和应用类等
2026-02-15 00:22:24 +08:00
ChestnutYueyue 387ea62853 refactor: 统一方法命名规范并优化代码文档
- 将单例方法名从getInstance统一改为get
- 重命名边界框相关方法为getBounds
- 重命名位置相关方法为setPos/getPos
- 优化代码注释,增加详细文档说明
- 移除调试信息输出
- 统一命名管理器类后缀为Mgr
- 重构节点相关方法名,如removeFromParent改为detach
- 统一坐标转换方法命名为toWorld/toLocal
2026-02-14 23:59:19 +08:00
ChestnutYueyue c6c90a7374 refactor: 移除音频系统、过渡场景和示例代码
- 删除音频引擎相关代码,包括AudioEngine和Sound类
- 移除所有过渡场景实现及基类
- 清理示例程序代码
- 简化构建配置,移除SDL2_mixer依赖
- 优化代码结构,移除不必要的资源管理代码
2026-02-14 22:37:09 +08:00
ChestnutYueyue 93d07e547f feat(视口适配): 添加视口适配器功能并集成到相机和输入系统
实现视口适配器功能,支持多种适配模式(宽高比、拉伸、居中、自定义)
将视口适配器集成到相机系统,实现坐标转换和视口自动调整
将视口适配器集成到输入系统,支持逻辑坐标转换
移除不再使用的字符串转换工具类
优化相机矩阵计算,支持旋转和缩放
添加数学工具函数,包括角度处理和坐标转换
2026-02-14 18:58:24 +08:00
ChestnutYueyue 2767d64bf8 refactor(core): 移除字符串编码转换工具并清理头文件包含
移除不再使用的字符串编码转换工具文件 string.h,并清理相关头文件包含关系
调整部分头文件中的代码格式,优化枚举和结构体定义
2026-02-14 17:49:00 +08:00
ChestnutYueyue b949d1a3da docs: 删除Extra2D构建系统文档和API教程文档
删除以下文档文件:
- docs/Extra2D构建系统文档.md
- docs/API_Tutorial/01_Quick_Start.md
- docs/API_Tutorial/02_Scene_System.md
- docs/API_Tutorial/03_Node_System.md
- docs/API_Tutorial/05_Input_Handling.md
- docs/API_Tutorial/06_Collision_Detection.md
- docs/API_Tutorial/08_Audio_System.md
- docs/API_Tutorial/09_Action_System.md
2026-02-14 17:43:50 +08:00
ChestnutYueyue 55c66e5038 refactor: 移除空间索引系统和UI组件
移除不再使用的空间索引系统(包括四叉树、空间哈希和空间管理器)以及相关的UI组件(如按钮、复选框、滑动条等)。这些功能已被更现代的解决方案取代,移除它们可以简化代码库并减少维护负担。

同时更新了示例代码,移除了对已删除组件的依赖,改为使用基本的形状节点展示功能。
2026-02-14 17:43:07 +08:00
ChestnutYueyue 313a56bf72 refactor: 移除示例游戏和动画系统相关代码
移除 push_box、flappy_bird 等示例游戏代码及相关资源文件
删除动画系统相关实现文件,包括动画解析器、帧渲染器等
清理不再使用的 Action 系统代码
移除 xmake.lua 中不再需要的示例项目配置
2026-02-14 17:23:58 +08:00
370 changed files with 21177 additions and 66537 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,133 +1,130 @@
#pragma once #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/core/types.h>
#include <extra2d/graphics/render_backend.h> #include <string>
#include <extra2d/platform/window.h>
#include <memory>
namespace extra2d { namespace extra2d {
// 前向声明 class IWindow;
class Input; class IInput;
class AudioEngine; class RenderBackend;
class SceneManager; class WindowModule;
class ResourceManager; class RenderModule;
class TimerManager; class InputModule;
class EventQueue;
class EventDispatcher;
class Camera;
// ============================================================================ /**
// Application 配置 * @brief
// ============================================================================ */
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 单例 - 应用主控
// ============================================================================
class Application { class Application {
public: public:
// Meyer's 单例 static Application &get();
static Application &instance();
// 禁止拷贝
Application(const Application &) = delete; Application(const Application &) = delete;
Application &operator=(const Application &) = delete; Application &operator=(const Application &) = delete;
// ------------------------------------------------------------------------ /**
// 生命周期 * @brief
// ------------------------------------------------------------------------ */
bool init(const AppConfig &config); 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(); void shutdown();
/**
* @brief
*/
void run(); void run();
/**
* @brief 退
*/
void quit(); void quit();
// ------------------------------------------------------------------------ /**
// 状态控制 * @brief
// ------------------------------------------------------------------------ */
void pause(); void pause();
/**
* @brief
*/
void resume(); void resume();
bool isPaused() const { return paused_; } bool isPaused() const { return paused_; }
bool isRunning() const { return running_; } bool isRunning() const { return running_; }
// ------------------------------------------------------------------------ /**
// 子系统访问 * @brief
// ------------------------------------------------------------------------ * @return
Window &window() { return *window_; } */
RenderBackend &renderer() { return *renderer_; } IWindow *window();
Input &input();
AudioEngine &audio();
SceneManager &scenes();
ResourceManager &resources();
TimerManager &timers();
EventQueue &eventQueue();
EventDispatcher &eventDispatcher();
Camera &camera();
// ------------------------------------------------------------------------ /**
// 便捷方法 * @brief
// ------------------------------------------------------------------------ * @return
*/
RenderBackend *renderer();
/**
* @brief
* @return
*/
IInput *input();
/**
* @brief
* @param scene
*/
void enterScene(Ptr<class Scene> scene); void enterScene(Ptr<class Scene> scene);
void enterScene(Ptr<class Scene> scene, Ptr<class TransitionScene> transitionScene);
float deltaTime() const { return deltaTime_; } float deltaTime() const { return deltaTime_; }
float totalTime() const { return totalTime_; } float totalTime() const { return totalTime_; }
int fps() const { return currentFps_; } int fps() const { return currentFps_; }
// 获取配置
const AppConfig &getConfig() const { return config_; }
private: private:
Application() = default; Application();
~Application(); ~Application();
void mainLoop(); void mainLoop();
void update(); void update();
void render(); 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 initialized_ = false;
bool running_ = false; bool running_ = false;
bool paused_ = false; bool paused_ = false;
bool shouldQuit_ = false; bool shouldQuit_ = false;
// 时间
float deltaTime_ = 0.0f; float deltaTime_ = 0.0f;
float totalTime_ = 0.0f; float totalTime_ = 0.0f;
double lastFrameTime_ = 0.0; double lastFrameTime_ = 0.0;

View File

@ -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

View File

@ -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

View File

@ -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}; inline constexpr Color Transparent{0.0f, 0.0f, 0.0f, 0.0f};
} // namespace Colors } // 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 } // namespace extra2d

View File

@ -330,6 +330,221 @@ inline float degrees(float radians) { return radians * RAD_TO_DEG; }
inline float radians(float degrees) { return degrees * DEG_TO_RAD; } 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 &center, 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 &center1, float radius1,
const Vec2 &center2, 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 math
} // namespace extra2d } // namespace extra2d

View File

@ -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

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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); \
}

View File

@ -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

View File

@ -7,10 +7,17 @@
namespace extra2d { 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 Ptr = std::shared_ptr<T>;
template <typename T> using SharedPtr = std::shared_ptr<T>;
template <typename T> using UniquePtr = std::unique_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)...); 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 的便捷函数 /// 创建 unique_ptr 的便捷函数
template <typename T, typename... Args> template <typename T, typename... Args>
inline UniquePtr<T> makeUnique(Args &&...args) { inline UniquePtr<T> makeUnique(Args &&...args) {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,114 +1,63 @@
#pragma once #pragma once
// Easy2D v3.0 - 统一入口头文件 // Extra2D - 统一入口头文件
// 包含所有公共 API // 包含所有公共 API
// Core // Core
#include <extra2d/core/types.h>
#include <extra2d/core/string.h>
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/math_types.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 // Platform
#include <extra2d/platform/window.h> #include <extra2d/platform/iinput.h>
#include <extra2d/platform/input.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 // Graphics
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/texture/font.h>
#include <extra2d/graphics/font.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/camera.h> #include <extra2d/graphics/core/render_module.h>
#include <extra2d/graphics/shader_system.h> #include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/graphics/texture/texture.h>
#include <extra2d/graphics/render_target.h> #include <extra2d/graphics/core/render_target.h>
#include <extra2d/graphics/vram_manager.h> #include <extra2d/graphics/camera/viewport_adapter.h>
#include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/graphics/texture/texture_pool.h>
// Scene // Scene
#include <extra2d/scene/node.h> #include <extra2d/scene/node.h>
#include <extra2d/scene/scene.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/scene_manager.h>
#include <extra2d/scene/transition_scene.h> #include <extra2d/scene/shape_node.h>
#include <extra2d/scene/transition_fade_scene.h> #include <extra2d/scene/sprite.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>
// Event // Event
#include <extra2d/event/event.h> #include <extra2d/event/event.h>
#include <extra2d/event/event_queue.h>
#include <extra2d/event/event_dispatcher.h> #include <extra2d/event/event_dispatcher.h>
#include <extra2d/event/input_codes.h> #include <extra2d/event/event_queue.h>
// Audio
#include <extra2d/audio/audio_engine.h>
#include <extra2d/audio/sound.h>
// Resource
#include <extra2d/resource/resource_manager.h>
// Utils // Utils
#include <extra2d/utils/logger.h>
#include <extra2d/utils/timer.h>
#include <extra2d/utils/data.h>
#include <extra2d/utils/random.h> #include <extra2d/utils/random.h>
#include <extra2d/utils/timer.h>
// Spatial // Services
#include <extra2d/spatial/spatial_index.h> #include <extra2d/services/event_service.h>
#include <extra2d/spatial/quadtree.h> #include <extra2d/services/scene_service.h>
#include <extra2d/spatial/spatial_hash.h> #include <extra2d/services/timer_service.h>
#include <extra2d/spatial/spatial_manager.h> #include <extra2d/services/camera_service.h>
#include <extra2d/services/logger_service.h>
// Effects
#include <extra2d/effects/post_process.h>
#include <extra2d/effects/particle_system.h>
#include <extra2d/effects/custom_effect_manager.h>
// Application // Application
#include <extra2d/app/application.h> #include <extra2d/app/application.h>
#ifdef __SWITCH__ #ifdef __SWITCH__
#include <switch.h> #include <switch.h>
#endif #endif

View File

@ -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 truefalse
*/
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,8 +1,11 @@
#pragma once #pragma once
#include <extra2d/graphics/opengl/gl_shader.h> #include <extra2d/graphics/backends/opengl/gl_buffer.h>
#include <extra2d/graphics/opengl/gl_sprite_batch.h> #include <extra2d/graphics/backends/opengl/gl_framebuffer.h>
#include <extra2d/graphics/render_backend.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 <array>
#include <glad/glad.h> #include <glad/glad.h>
@ -10,7 +13,10 @@
namespace extra2d { namespace extra2d {
class Window; // 前向声明
class IWindow;
class GLContext;
class GLFramebuffer;
// ============================================================================ // ============================================================================
// OpenGL 渲染器实现 // OpenGL 渲染器实现
@ -21,7 +27,7 @@ public:
~GLRenderer() override; ~GLRenderer() override;
// RenderBackend 接口实现 // RenderBackend 接口实现
bool init(Window *window) override; bool init(IWindow* window) override;
void shutdown() override; void shutdown() override;
void beginFrame(const Color &clearColor) override; void beginFrame(const Color &clearColor) override;
@ -48,6 +54,7 @@ public:
void drawSprite(const Texture &texture, const Vec2 &position, void drawSprite(const Texture &texture, const Vec2 &position,
const Color &tint) override; const Color &tint) override;
void endSpriteBatch() override; void endSpriteBatch() override;
void flush() override;
void drawLine(const Vec2 &start, const Vec2 &end, const Color &color, void drawLine(const Vec2 &start, const Vec2 &end, const Color &color,
float width) override; float width) override;
@ -76,6 +83,42 @@ public:
Stats getStats() const override { return stats_; } Stats getStats() const override { return stats_; }
void resetStats() override; 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: private:
// 形状批处理常量 // 形状批处理常量
static constexpr size_t MAX_CIRCLE_SEGMENTS = 128; static constexpr size_t MAX_CIRCLE_SEGMENTS = 128;
@ -88,14 +131,14 @@ private:
float r, g, b, a; float r, g, b, a;
}; };
Window *window_; IWindow* window_;
GLSpriteBatch spriteBatch_; GLSpriteBatch spriteBatch_;
GLShader shapeShader_; Ptr<IShader> shapeShader_;
GLuint shapeVao_; GLuint shapeVao_; // 形状 VAO手动管理用于顶点属性配置
GLuint shapeVbo_; GLBuffer shapeBuffer_; // 形状 VBO使用 GLBuffer 管理)
GLuint lineVao_; // 线条专用 VAO GLuint lineVao_; // 线条 VAO(手动管理,用于顶点属性配置)
GLuint lineVbo_; // 线条专用 VBO GLBuffer lineBuffer_; // 线条 VBO使用 GLBuffer 管理)
glm::mat4 viewProjection_; glm::mat4 viewProjection_;
std::vector<glm::mat4> transformStack_; std::vector<glm::mat4> transformStack_;
@ -112,15 +155,23 @@ private:
size_t lineVertexCount_ = 0; size_t lineVertexCount_ = 0;
float currentLineWidth_ = 1.0f; float currentLineWidth_ = 1.0f;
// OpenGL 状态缓存 // OpenGL 管线状态管理
BlendMode cachedBlendMode_ = BlendMode::None; GLPipeline pipeline_;
bool blendEnabled_ = false;
int cachedViewportX_ = 0; // 自动批处理状态
int cachedViewportY_ = 0; bool batchActive_ = false; // 批处理是否激活
int cachedViewportWidth_ = 0; bool autoBatchEnabled_ = true; // 是否启用自动批处理
int cachedViewportHeight_ = 0; 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 initShapeRendering();
void ensureBatchActive(); // 确保批处理已激活
void submitPendingSprites(); // 提交待处理的精灵
void flushShapeBatch(); void flushShapeBatch();
void flushLineBatch(); void flushLineBatch();
void addShapeVertex(float x, float y, const Color &color); void addShapeVertex(float x, float y, const Color &color);

View File

@ -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 truefalse
*/
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 truefalse
*/
bool compileFromSource(const char* vertexSource, const char* fragmentSource);
/**
* @brief Shader
* @param binary
* @return truefalse
*/
bool compileFromBinary(const std::vector<uint8_t>& binary);
/**
* @brief Shader二进制数据
* @param outBinary
* @return truefalse
*/
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 ID0
*/
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 truefalse
*/
bool getShaderBinary(const IShader& shader,
std::vector<uint8_t>& outBinary) override;
};
} // namespace extra2d

View File

@ -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

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/texture/texture.h>
#include <extra2d/graphics/alpha_mask.h> #include <extra2d/graphics/texture/alpha_mask.h>
#include <glad/glad.h> #include <glad/glad.h>

View File

@ -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 &center, float radius, const Color &color,
int segments, float width) override;
void fillCircle(const Vec2 &center, 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

View File

@ -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

View File

@ -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

View File

@ -7,6 +7,8 @@
namespace extra2d { namespace extra2d {
class ViewportAdapter;
// ============================================================================ // ============================================================================
// 2D 正交相机 // 2D 正交相机
// ============================================================================ // ============================================================================
@ -20,8 +22,8 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 位置和变换 // 位置和变换
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void setPosition(const Vec2 &position); void setPos(const Vec2 &position);
void setPosition(float x, float y); void setPos(float x, float y);
Vec2 getPosition() const { return position_; } Vec2 getPosition() const { return position_; }
void setRotation(float degrees); void setRotation(float degrees);
@ -65,6 +67,20 @@ public:
void clearBounds(); void clearBounds();
void clampToBounds(); void clampToBounds();
// ------------------------------------------------------------------------
// 视口适配器
// ------------------------------------------------------------------------
/**
* @brief
* @param adapter
*/
void setViewportAdapter(ViewportAdapter *adapter);
/**
* @brief
*/
void applyViewportAdapter();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 快捷方法:看向某点 // 快捷方法:看向某点
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -83,6 +99,8 @@ private:
Rect bounds_; Rect bounds_;
bool hasBounds_ = false; bool hasBounds_ = false;
ViewportAdapter *viewportAdapter_ = nullptr;
mutable glm::mat4 viewMatrix_; mutable glm::mat4 viewMatrix_;
mutable glm::mat4 projMatrix_; mutable glm::mat4 projMatrix_;
mutable glm::mat4 vpMatrix_; mutable glm::mat4 vpMatrix_;

View File

@ -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

View File

@ -3,12 +3,13 @@
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/graphics/resources/pipeline.h>
#include <glm/mat4x4.hpp> #include <glm/mat4x4.hpp>
namespace extra2d { namespace extra2d {
// 前向声明 // 前向声明
class Window; class IWindow;
class Texture; class Texture;
class FontAtlas; class FontAtlas;
class Shader; class Shader;
@ -24,15 +25,7 @@ enum class BackendType {
// D3D12 // D3D12
}; };
// ============================================================================ // BlendMode 定义在 pipeline.h 中
// 混合模式
// ============================================================================
enum class BlendMode {
None, // 不混合
Alpha, // 标准 Alpha 混合
Additive, // 加法混合
Multiply // 乘法混合
};
// ============================================================================ // ============================================================================
// 渲染后端抽象接口 // 渲染后端抽象接口
@ -44,7 +37,7 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 生命周期 // 生命周期
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual bool init(Window *window) = 0; virtual bool init(IWindow* window) = 0;
virtual void shutdown() = 0; virtual void shutdown() = 0;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -78,6 +71,10 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 精灵批渲染 // 精灵批渲染
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/**
* @brief
* @note drawSprite/drawText
*/
virtual void beginSpriteBatch() = 0; virtual void beginSpriteBatch() = 0;
virtual void drawSprite(const Texture &texture, const Rect &destRect, virtual void drawSprite(const Texture &texture, const Rect &destRect,
const Rect &srcRect, const Color &tint, const Rect &srcRect, const Color &tint,
@ -86,6 +83,12 @@ public:
const Color &tint) = 0; const Color &tint) = 0;
virtual void endSpriteBatch() = 0; virtual void endSpriteBatch() = 0;
/**
* @brief
* @note
*/
virtual void flush() = 0;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 形状渲染 // 形状渲染
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -3,7 +3,7 @@
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/texture/texture.h>
#include <glm/mat4x4.hpp> #include <glm/mat4x4.hpp>
#include <cstdint> #include <cstdint>
#include <variant> #include <variant>

View File

@ -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

View File

@ -2,8 +2,8 @@
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/graphics/opengl/gl_texture.h> #include <extra2d/graphics/backends/opengl/gl_texture.h>
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/texture/texture.h>
#include <mutex> #include <mutex>
namespace extra2d { namespace extra2d {
@ -16,7 +16,6 @@ struct RenderTargetConfig {
int height = 600; // 高度 int height = 600; // 高度
PixelFormat colorFormat = PixelFormat::RGBA8; // 颜色格式 PixelFormat colorFormat = PixelFormat::RGBA8; // 颜色格式
bool hasDepth = true; // 是否包含深度缓冲 bool hasDepth = true; // 是否包含深度缓冲
bool hasDepthBuffer = true; // 兼容旧API的别名 (同hasDepth)
bool hasStencil = false; // 是否包含模板缓冲 bool hasStencil = false; // 是否包含模板缓冲
int samples = 1; // 多重采样数 (1 = 无MSAA) int samples = 1; // 多重采样数 (1 = 无MSAA)
bool autoResize = true; // 是否自动调整大小 bool autoResize = true; // 是否自动调整大小
@ -47,11 +46,6 @@ public:
*/ */
bool create(const RenderTargetConfig &config); bool create(const RenderTargetConfig &config);
/**
* @brief create的别名API
*/
bool init(const RenderTargetConfig &config) { return create(config); }
/** /**
* @brief * @brief
*/ */
@ -62,11 +56,6 @@ public:
*/ */
void destroy(); void destroy();
/**
* @brief destroy的别名API
*/
void shutdown() { destroy(); }
/** /**
* @brief * @brief
*/ */
@ -144,14 +133,6 @@ public:
*/ */
void copyTo(RenderTarget &target); void copyTo(RenderTarget &target);
/**
* @brief blitTo的别名API
* @param target
* @param color
* @param depth
*/
void blitTo(RenderTarget &target, bool color = true, bool depth = false);
/** /**
* @brief * @brief
*/ */
@ -233,7 +214,7 @@ private:
// ============================================================================ // ============================================================================
class RenderTargetStack { class RenderTargetStack {
public: public:
static RenderTargetStack &getInstance(); static RenderTargetStack &get();
/** /**
* @brief * @brief
@ -271,12 +252,12 @@ private:
// ============================================================================ // ============================================================================
// 渲染目标管理器 - 全局渲染目标管理 // 渲染目标管理器 - 全局渲染目标管理
// ============================================================================ // ============================================================================
class RenderTargetManager { class RenderTargetMgr {
public: public:
/** /**
* @brief * @brief
*/ */
static RenderTargetManager &getInstance(); static RenderTargetMgr& get();
/** /**
* @brief * @brief
@ -313,10 +294,10 @@ public:
bool isInitialized() const { return initialized_; } bool isInitialized() const { return initialized_; }
private: private:
RenderTargetManager() = default; RenderTargetMgr() = default;
~RenderTargetManager() = default; ~RenderTargetMgr() = default;
RenderTargetManager(const RenderTargetManager &) = delete; RenderTargetMgr(const RenderTargetMgr &) = delete;
RenderTargetManager &operator=(const RenderTargetManager &) = delete; RenderTargetMgr &operator=(const RenderTargetMgr &) = delete;
Ptr<RenderTarget> defaultRenderTarget_; Ptr<RenderTarget> defaultRenderTarget_;
std::vector<Ptr<RenderTarget>> renderTargets_; std::vector<Ptr<RenderTarget>> renderTargets_;
@ -326,8 +307,7 @@ private:
// ============================================================================ // ============================================================================
// 便捷宏 // 便捷宏
// ============================================================================ // ============================================================================
#define E2D_RENDER_TARGET_STACK() ::extra2d::RenderTargetStack::getInstance() #define E2D_RENDER_TARGET_STACK() ::extra2d::RenderTargetStack::get()
#define E2D_RENDER_TARGET_MANAGER() \ #define E2D_RENDER_TARGET_MGR() ::extra2d::RenderTargetMgr::get()
::extra2d::RenderTargetManager::getInstance()
} // namespace extra2d } // namespace extra2d

View File

@ -13,7 +13,7 @@ namespace extra2d {
class GPUContext { class GPUContext {
public: public:
/// 获取单例实例 /// 获取单例实例
static GPUContext& getInstance(); static GPUContext& get();
/// 标记 GPU 上下文为有效(在初始化完成后调用) /// 标记 GPU 上下文为有效(在初始化完成后调用)
void markValid(); void markValid();

View File

@ -9,9 +9,9 @@ namespace extra2d {
// ============================================================================ // ============================================================================
// VRAM 管理器 - 跟踪显存使用情况 // VRAM 管理器 - 跟踪显存使用情况
// ============================================================================ // ============================================================================
class VRAMManager { class VRAMMgr {
public: public:
static VRAMManager& getInstance(); static VRAMMgr& get();
// 纹理显存跟踪 // 纹理显存跟踪
void allocTexture(size_t size); void allocTexture(size_t size);
@ -39,10 +39,10 @@ public:
void reset(); void reset();
private: private:
VRAMManager(); VRAMMgr();
~VRAMManager() = default; ~VRAMMgr() = default;
VRAMManager(const VRAMManager&) = delete; VRAMMgr(const VRAMMgr&) = delete;
VRAMManager& operator=(const VRAMManager&) = delete; VRAMMgr& operator=(const VRAMMgr&) = delete;
mutable std::mutex mutex_; mutable std::mutex mutex_;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 truefalse
*/
bool init(const std::string& cacheDir);
/**
* @brief
*/
void shutdown();
/**
* @brief
* @param name Shader名称
* @param sourceHash
* @return truefalse
*/
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 truefalse
*/
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 truefalse
*/
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 truefalse
*/
bool loadCacheIndex();
/**
* @brief
* @return truefalse
*/
bool saveCacheIndex();
/**
* @brief
* @param name Shader名称
* @return
*/
std::string getCachePath(const std::string& name) const;
/**
* @brief
* @return truefalse
*/
bool ensureCacheDirectory();
};
// 便捷宏
#define E2D_SHADER_CACHE() ::extra2d::ShaderCache::getInstance()
} // namespace extra2d

View File

@ -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 truefalse
*/
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 truefalse
*/
bool isEnabled() const { return enabled_; }
/**
* @brief
* @return truefalse
*/
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

View File

@ -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 truefalse
*/
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 truefalse
*/
virtual bool getShaderBinary(const IShader& shader,
std::vector<uint8_t>& outBinary) = 0;
};
} // namespace extra2d

View File

@ -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 truefalse
*/
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 truefalse
*/
bool parseCombinedFile(const std::string& content,
std::string& outVert,
std::string& outFrag,
ShaderMetadata& outMetadata);
/**
* @brief JSON块
* @param jsonContent JSON内容
* @param outMetadata
* @return truefalse
*/
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

View File

@ -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 truefalse
*/
bool init(Ptr<IShaderFactory> factory, const std::string& appName = "extra2d");
/**
* @brief Shader系统
* @param shaderDir Shader文件目录
* @param cacheDir
* @param factory Shader工厂
* @return truefalse
*/
bool init(const std::string& shaderDir,
const std::string& cacheDir,
Ptr<IShaderFactory> factory);
/**
* @brief Shader系统
*/
void shutdown();
/**
* @brief
* @return truefalse
*/
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 truefalse
*/
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 truefalse
*/
bool isHotReloadEnabled() const;
/**
* @brief
*/
void update();
/**
* @brief Shader
* @param name Shader名称
* @return truefalse
*/
bool reload(const std::string& name);
// ------------------------------------------------------------------------
// 内置Shader
// ------------------------------------------------------------------------
/**
* @brief Shader
* @param name Shader名称
* @return Shader实例
*/
Ptr<IShader> getBuiltin(const std::string& name);
/**
* @brief Shader
* @return truefalse
*/
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

View File

@ -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

View File

@ -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

View File

@ -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 Shadernullptr
*/
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 Shadernullptr
*/
Ptr<GLShader> loadFromSource(const std::string &name,
const std::string &vertSource,
const std::string &fragSource);
/**
* @brief Shader
* @param name Shader名称
* @return Shadernullptr
*/
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

View File

@ -3,8 +3,8 @@
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/texture/texture.h>
#include <extra2d/graphics/opengl/gl_texture.h> #include <extra2d/graphics/backends/opengl/gl_texture.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
@ -146,9 +146,9 @@ private:
/** /**
* @brief * @brief
*/ */
class TextureAtlasManager { class TextureAtlasMgr {
public: public:
static TextureAtlasManager& getInstance(); static TextureAtlasMgr& get();
// 获取主图集 // 获取主图集
TextureAtlas& getAtlas() { return atlas_; } TextureAtlas& getAtlas() { return atlas_; }
@ -172,11 +172,11 @@ public:
} }
private: private:
TextureAtlasManager() = default; TextureAtlasMgr() = default;
~TextureAtlasManager() = default; ~TextureAtlasMgr() = default;
TextureAtlasManager(const TextureAtlasManager&) = delete; TextureAtlasMgr(const TextureAtlasMgr&) = delete;
TextureAtlasManager& operator=(const TextureAtlasManager&) = delete; TextureAtlasMgr& operator=(const TextureAtlasMgr&) = delete;
TextureAtlas atlas_; TextureAtlas atlas_;
}; };

View File

@ -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 &region,
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 &region,
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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; }
};
}

View File

@ -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

View File

@ -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

View File

@ -1,12 +1,11 @@
#pragma once #pragma once
#include <algorithm>
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/event/event_dispatcher.h> #include <extra2d/event/event_dispatcher.h>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <functional> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
@ -14,7 +13,6 @@ namespace extra2d {
// 前向声明 // 前向声明
class Scene; class Scene;
class Action;
class RenderBackend; class RenderBackend;
struct RenderCommand; struct RenderCommand;
@ -39,19 +37,19 @@ public:
void removeChild(Ptr<Node> child); void removeChild(Ptr<Node> child);
void removeChildByName(const std::string &name); void removeChildByName(const std::string &name);
void removeFromParent(); void detach();
void removeAllChildren(); void clearChildren();
Ptr<Node> getParent() const { return parent_.lock(); } Ptr<Node> getParent() const { return parent_.lock(); }
const std::vector<Ptr<Node>> &getChildren() const { return children_; } const std::vector<Ptr<Node>> &getChildren() const { return children_; }
Ptr<Node> getChildByName(const std::string &name) const; Ptr<Node> findChild(const std::string &name) const;
Ptr<Node> getChildByTag(int tag) const; Ptr<Node> findChildByTag(int tag) const;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 变换属性 // 变换属性
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void setPosition(const Vec2 &pos); void setPos(const Vec2 &pos);
void setPosition(float x, float y); void setPos(float x, float y);
Vec2 getPosition() const { return position_; } Vec2 getPosition() const { return position_; }
void setRotation(float degrees); void setRotation(float degrees);
@ -80,7 +78,7 @@ public:
* @brief * @brief
* @param color RGB颜色 * @param color RGB颜色
*/ */
void setColor(const Color3B& color); void setColor(const Color3B &color);
Color3B getColor() const { return color_; } Color3B getColor() const { return color_; }
/** /**
@ -101,8 +99,8 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 世界变换 // 世界变换
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
Vec2 convertToWorldSpace(const Vec2 &localPos) const; Vec2 toWorld(const Vec2 &localPos) const;
Vec2 convertToNodeSpace(const Vec2 &worldPos) const; Vec2 toLocal(const Vec2 &worldPos) const;
glm::mat4 getLocalTransform() const; glm::mat4 getLocalTransform() const;
glm::mat4 getWorldTransform() const; glm::mat4 getWorldTransform() const;
@ -116,7 +114,7 @@ public:
* @brief * @brief
* *
*/ */
void batchUpdateTransforms(); void batchTransforms();
/** /**
* @brief * @brief
@ -144,68 +142,9 @@ public:
virtual void onDetachFromScene(); virtual void onDetachFromScene();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 边界框(用于空间索引) // 边界框
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual Rect getBoundingBox() const; virtual Rect getBounds() 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;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 事件系统 // 事件系统
@ -243,63 +182,58 @@ protected:
private: private:
// ========================================================================== // ==========================================================================
// 成员变量按类型大小降序排列,减少内存对齐填充 // 成员变量按类型大小降序排列,减少内存对齐填充
// 64位系统对齐std::string(32) > glm::mat4(64) > std::vector(24) > // 64位系统对齐std::string(32) > glm::mat4(64) > std::vector(24) >
// double(8) > float(4) > int(4) > bool(1) // double(8) > float(4) > int(4) > bool(1)
// ========================================================================== // ==========================================================================
// 1. 大块内存64字节 // 1. 大块内存64字节
mutable glm::mat4 localTransform_; // 64 bytes mutable glm::mat4 localTransform_; // 64 bytes
mutable glm::mat4 worldTransform_; // 64 bytes mutable glm::mat4 worldTransform_; // 64 bytes
// 2. 字符串和容器24-32字节 // 2. 字符串和容器24-32字节
std::string name_; // 32 bytes std::string name_; // 32 bytes
std::vector<Ptr<Node>> children_; // 24 bytes std::vector<Ptr<Node>> children_; // 24 bytes
// 3. 子节点索引(加速查找) // 3. 子节点索引(加速查找)
std::unordered_map<std::string, WeakPtr<Node>> nameIndex_; // 56 bytes std::unordered_map<std::string, WeakPtr<Node>> nameIndex_; // 56 bytes
std::unordered_map<int, WeakPtr<Node>> tagIndex_; // 56 bytes std::unordered_map<int, WeakPtr<Node>> tagIndex_; // 56 bytes
// 4. 事件分发器 // 4. 事件分发器
EventDispatcher eventDispatcher_; // 大小取决于实现 EventDispatcher eventDispatcher_; // 大小取决于实现
// 5. 父节点引用 // 5. 父节点引用
WeakPtr<Node> parent_; // 16 bytes WeakPtr<Node> parent_; // 16 bytes
// 7. 变换属性(按访问频率分组) // 7. 变换属性(按访问频率分组)
Vec2 position_ = Vec2::Zero(); // 8 bytes Vec2 position_ = Vec2::Zero(); // 8 bytes
Vec2 scale_ = Vec2(1.0f, 1.0f); // 8 bytes Vec2 scale_ = Vec2(1.0f, 1.0f); // 8 bytes
Vec2 anchor_ = Vec2(0.5f, 0.5f); // 8 bytes Vec2 anchor_ = Vec2(0.5f, 0.5f); // 8 bytes
Vec2 skew_ = Vec2::Zero(); // 8 bytes Vec2 skew_ = Vec2::Zero(); // 8 bytes
// 8. 边界框(用于空间索引) // 8. 浮点属性
Rect lastSpatialBounds_; // 16 bytes float rotation_ = 0.0f; // 4 bytes
float opacity_ = 1.0f; // 4 bytes
// 9. 浮点属性
float rotation_ = 0.0f; // 4 bytes
float opacity_ = 1.0f; // 4 bytes
// 10. 颜色属性 // 10. 颜色属性
Color3B color_ = Color3B(255, 255, 255); // 3 bytes Color3B color_ = Color3B(255, 255, 255); // 3 bytes
// 11. 整数属性 // 11. 整数属性
int zOrder_ = 0; // 4 bytes int zOrder_ = 0; // 4 bytes
int tag_ = -1; // 4 bytes int tag_ = -1; // 4 bytes
// 12. 布尔属性 // 12. 布尔属性
bool flipX_ = false; // 1 byte bool flipX_ = false; // 1 byte
bool flipY_ = false; // 1 byte bool flipY_ = false; // 1 byte
// 11. 场景指针 // 13. 场景指针
Scene *scene_ = nullptr; // 8 bytes Scene *scene_ = nullptr; // 8 bytes
// 12. 布尔标志(打包在一起) // 14. 布尔标志(打包在一起)
mutable bool transformDirty_ = true; // 1 byte mutable bool transformDirty_ = true; // 1 byte
mutable bool worldTransformDirty_ = true; // 1 byte mutable bool worldTransformDirty_ = true; // 1 byte
bool childrenOrderDirty_ = false; // 1 byte bool childrenOrderDirty_ = false; // 1 byte
bool visible_ = true; // 1 byte bool visible_ = true; // 1 byte
bool running_ = false; // 1 byte bool running_ = false; // 1 byte
bool spatialIndexed_ = true; // 1 byte
// 填充 2 bytes 到 8 字节对齐
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -1,9 +1,8 @@
#pragma once #pragma once
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/graphics/camera.h> #include <extra2d/graphics/camera/camera.h>
#include <extra2d/scene/node.h> #include <extra2d/scene/node.h>
#include <extra2d/spatial/spatial_manager.h>
#include <vector> #include <vector>
namespace extra2d { namespace extra2d {
@ -61,28 +60,6 @@ public:
void collectRenderCommands(std::vector<RenderCommand> &commands, void collectRenderCommands(std::vector<RenderCommand> &commands,
int parentZOrder = 0) override; 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_; Ptr<Camera> defaultCamera_;
bool paused_ = false; bool paused_ = false;
// 空间索引系统
SpatialManager spatialManager_;
bool spatialIndexingEnabled_ = true;
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -2,7 +2,7 @@
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/scene/scene.h> #include <extra2d/scene/scene.h>
#include <extra2d/scene/transition_scene.h>
#include <functional> #include <functional>
#include <stack> #include <stack>
#include <string> #include <string>
@ -11,65 +11,89 @@
namespace extra2d { namespace extra2d {
// 前向声明
struct RenderCommand; struct RenderCommand;
class TransitionScene; class TransitionScene;
enum class TransitionType;
// ============================================================================ /**
// 场景管理器 - 管理场景的生命周期和切换 * @brief -
// ============================================================================ */
class SceneManager { class SceneManager {
public: public:
using TransitionCallback = std::function<void()>; using TransitionCallback = std::function<void()>;
// ------------------------------------------------------------------------ static SceneManager &get();
// 单例访问
// ------------------------------------------------------------------------
static SceneManager &getInstance();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 场景操作 // 基本场景操作(无过渡效果)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 运行第一个场景
void runWithScene(Ptr<Scene> scene); void runWithScene(Ptr<Scene> scene);
// 替换当前场景
void replaceScene(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, void replaceScene(Ptr<Scene> scene, TransitionType transition,
float duration = 0.5f); float duration = 0.5f);
// 压入新场景(当前场景暂停) /**
void pushScene(Ptr<Scene> scene); * @brief
* @param scene
* @param transition
* @param duration
*/
void pushScene(Ptr<Scene> scene, TransitionType transition, void pushScene(Ptr<Scene> scene, TransitionType transition,
float duration = 0.5f); float duration = 0.5f);
// 弹出当前场景(恢复上一个场景) /**
void popScene(); * @brief
* @param transition
* @param duration
*/
void popScene(TransitionType transition, float duration = 0.5f); void popScene(TransitionType transition, float duration = 0.5f);
// 弹出到根场景 /**
void popToRootScene(); * @brief
* @param transition
* @param duration
*/
void popToRootScene(TransitionType transition, float duration = 0.5f); 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, void popToScene(const std::string &name, TransitionType transition,
float duration = 0.5f); float duration = 0.5f);
/**
* @brief 使
* @param scene
* @param transitionScene
*/
void enterScene(Ptr<Scene> scene, Ptr<TransitionScene> transitionScene);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 获取场景 // 场景查询
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
Ptr<Scene> getCurrentScene() const; Ptr<Scene> getCurrentScene() const;
Ptr<Scene> getPreviousScene() const; Ptr<Scene> getPreviousScene() const;
Ptr<Scene> getRootScene() const; Ptr<Scene> getRootScene() const;
// 通过名称获取场景
Ptr<Scene> getSceneByName(const std::string &name) const; Ptr<Scene> getSceneByName(const std::string &name) const;
// ------------------------------------------------------------------------
// 查询
// ------------------------------------------------------------------------
size_t getSceneCount() const { return sceneStack_.size(); } size_t getSceneCount() const { return sceneStack_.size(); }
bool isEmpty() const { return sceneStack_.empty(); } bool isEmpty() const { return sceneStack_.empty(); }
bool hasScene(const std::string &name) const; bool hasScene(const std::string &name) const;
@ -82,16 +106,13 @@ public:
void collectRenderCommands(std::vector<RenderCommand> &commands); void collectRenderCommands(std::vector<RenderCommand> &commands);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 过渡控制 // 过渡状态
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool isTransitioning() const { return isTransitioning_; } bool isTransitioning() const { return isTransitioning_; }
void setTransitionCallback(TransitionCallback callback) { void setTransitionCallback(TransitionCallback callback) {
transitionCallback_ = callback; transitionCallback_ = callback;
} }
// ------------------------------------------------------------------------
// 清理
// ------------------------------------------------------------------------
void end(); void end();
void purgeCachedScenes(); void purgeCachedScenes();
@ -101,36 +122,50 @@ public:
SceneManager(const SceneManager &) = delete; SceneManager(const SceneManager &) = delete;
SceneManager &operator=(const SceneManager &) = delete; SceneManager &operator=(const SceneManager &) = delete;
// 场景切换(供 Application 使用)
void enterScene(Ptr<Scene> scene); void enterScene(Ptr<Scene> scene);
void enterScene(Ptr<Scene> scene, Ptr<TransitionScene> transitionScene);
private: private:
void doSceneSwitch(); /**
* @brief
* @param from
* @param to
* @param type
* @param duration
* @param stackAction
*/
void startTransition(Ptr<Scene> from, Ptr<Scene> to, TransitionType type, void startTransition(Ptr<Scene> from, Ptr<Scene> to, TransitionType type,
float duration, Function<void()> stackAction); float duration, std::function<void()> stackAction);
void finishTransition();
void dispatchPointerEvents(Scene &scene);
// 创建过渡场景 /**
Ptr<TransitionScene> createTransitionScene(TransitionType type, * @brief
float duration, * @param type
Ptr<Scene> inScene); * @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::stack<Ptr<Scene>> sceneStack_;
std::unordered_map<std::string, Ptr<Scene>> namedScenes_; std::unordered_map<std::string, Ptr<Scene>> namedScenes_;
// Transition state
bool isTransitioning_ = false; bool isTransitioning_ = false;
TransitionType currentTransition_ = TransitionType::None;
Ptr<TransitionScene> activeTransitionScene_;
Function<void()> transitionStackAction_;
TransitionCallback transitionCallback_; TransitionCallback transitionCallback_;
// Next scene to switch to (queued during transition)
Ptr<Scene> nextScene_; Ptr<Scene> nextScene_;
bool sendCleanupToScene_ = false; bool sendCleanupToScene_ = false;
Ptr<TransitionScene> activeTransitionScene_;
std::function<void()> transitionStackAction_;
Node *hoverTarget_ = nullptr; Node *hoverTarget_ = nullptr;
Node *captureTarget_ = nullptr; Node *captureTarget_ = nullptr;
Vec2 lastPointerWorld_ = Vec2::Zero(); Vec2 lastPointerWorld_ = Vec2::Zero();

View File

@ -85,7 +85,7 @@ public:
void addPoint(const Vec2 &point); void addPoint(const Vec2 &point);
void clearPoints(); void clearPoints();
Rect getBoundingBox() const override; Rect getBounds() const override;
protected: protected:
void onDraw(RenderBackend &renderer) override; void onDraw(RenderBackend &renderer) override;

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/texture/texture.h>
#include <extra2d/scene/node.h> #include <extra2d/scene/node.h>
namespace extra2d { namespace extra2d {
@ -37,7 +37,7 @@ public:
static Ptr<Sprite> create(Ptr<Texture> texture); static Ptr<Sprite> create(Ptr<Texture> texture);
static Ptr<Sprite> create(Ptr<Texture> texture, const Rect &rect); static Ptr<Sprite> create(Ptr<Texture> texture, const Rect &rect);
Rect getBoundingBox() const override; Rect getBounds() const override;
protected: protected:
void onDraw(RenderBackend &renderer) override; void onDraw(RenderBackend &renderer) override;

Some files were not shown because too many files have changed in this diff Show More