refactor: 重构 graphics 模块目录结构并简化配置系统
- 重组 graphics 目录,按功能分为 core/camera/shader/texture/memory 子目录
- 移除所有模块级 *_config.h 配置文件,改用模块内部的 Cfg 结构体
- 移除 config_loader 和 debug_config 相关文件
- 简化模块系统,使用 Module 基类替代复杂的 IModuleConfig/IModuleInitializer
- 添加 SDL_GetBasePath() 支持跨平台 shader 路径解析
- 修复日志宏不支持 {} 格式化语法的问题
- 更新文档反映新的目录结构
This commit is contained in:
parent
6273f3235d
commit
0425425ec7
|
|
@ -1,231 +1,143 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/core/registry.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/config/app_config.h>
|
||||
#include <extra2d/config/config_manager.h>
|
||||
#include <extra2d/config/module_config.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class IWindow;
|
||||
class IInput;
|
||||
class RenderBackend;
|
||||
class WindowModule;
|
||||
class RenderModule;
|
||||
class InputModule;
|
||||
|
||||
/**
|
||||
* @brief 应用程序类
|
||||
* 使用服务定位器模式管理模块依赖,支持依赖注入和测试Mock
|
||||
*/
|
||||
class Application {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 应用程序实例引用
|
||||
*/
|
||||
static Application& get();
|
||||
|
||||
|
||||
Application(const Application&) = delete;
|
||||
Application& operator=(const Application&) = delete;
|
||||
|
||||
|
||||
/**
|
||||
* @brief 使用默认配置初始化
|
||||
* @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 使用指定配置初始化
|
||||
* @brief 初始化(带配置)
|
||||
* @param config 应用配置
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
bool init(const AppConfig& config);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 使用配置文件初始化
|
||||
* @param configPath 配置文件路径
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
bool init(const std::string& configPath);
|
||||
|
||||
/**
|
||||
* @brief 关闭应用程序
|
||||
* @brief 关闭
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
|
||||
/**
|
||||
* @brief 运行主循环
|
||||
*/
|
||||
void run();
|
||||
|
||||
|
||||
/**
|
||||
* @brief 请求退出
|
||||
*/
|
||||
void quit();
|
||||
|
||||
|
||||
/**
|
||||
* @brief 暂停应用程序
|
||||
* @brief 暂停
|
||||
*/
|
||||
void pause();
|
||||
|
||||
|
||||
/**
|
||||
* @brief 恢复应用程序
|
||||
* @brief 恢复
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* @brief 检查是否暂停
|
||||
* @return 暂停状态返回 true
|
||||
*/
|
||||
|
||||
bool isPaused() const { return paused_; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否运行中
|
||||
* @return 运行中返回 true
|
||||
*/
|
||||
bool isRunning() const { return running_; }
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取窗口
|
||||
* @return 窗口引用
|
||||
* @return 窗口指针
|
||||
*/
|
||||
IWindow& window() { return *window_; }
|
||||
|
||||
IWindow* window();
|
||||
|
||||
/**
|
||||
* @brief 获取渲染器
|
||||
* @return 渲染器引用
|
||||
* @return 渲染器指针
|
||||
*/
|
||||
RenderBackend& renderer();
|
||||
|
||||
RenderBackend* renderer();
|
||||
|
||||
/**
|
||||
* @brief 获取输入接口
|
||||
* @return 输入接口引用
|
||||
* @brief 获取输入
|
||||
* @return 输入指针
|
||||
*/
|
||||
IInput& input();
|
||||
|
||||
/**
|
||||
* @brief 获取场景服务
|
||||
* @return 场景服务共享指针
|
||||
*/
|
||||
SharedPtr<class ISceneService> scenes();
|
||||
|
||||
/**
|
||||
* @brief 获取计时器服务
|
||||
* @return 计时器服务共享指针
|
||||
*/
|
||||
SharedPtr<class ITimerService> timers();
|
||||
|
||||
/**
|
||||
* @brief 获取事件服务
|
||||
* @return 事件服务共享指针
|
||||
*/
|
||||
SharedPtr<class IEventService> events();
|
||||
|
||||
/**
|
||||
* @brief 获取相机服务
|
||||
* @return 相机服务共享指针
|
||||
*/
|
||||
SharedPtr<class ICameraService> camera();
|
||||
|
||||
IInput* input();
|
||||
|
||||
/**
|
||||
* @brief 进入场景
|
||||
* @param scene 场景指针
|
||||
*/
|
||||
void enterScene(Ptr<class Scene> scene);
|
||||
|
||||
/**
|
||||
* @brief 获取帧间隔时间
|
||||
* @return 帧间隔时间(秒)
|
||||
*/
|
||||
|
||||
float deltaTime() const { return deltaTime_; }
|
||||
|
||||
/**
|
||||
* @brief 获取总运行时间
|
||||
* @return 总运行时间(秒)
|
||||
*/
|
||||
float totalTime() const { return totalTime_; }
|
||||
|
||||
/**
|
||||
* @brief 获取当前帧率
|
||||
* @return 帧率
|
||||
*/
|
||||
int fps() const { return currentFps_; }
|
||||
|
||||
/**
|
||||
* @brief 获取配置管理器
|
||||
* @return 配置管理器引用
|
||||
*/
|
||||
ConfigManager& config();
|
||||
|
||||
/**
|
||||
* @brief 获取应用配置
|
||||
* @return 应用配置常量引用
|
||||
*/
|
||||
const AppConfig& getConfig() const;
|
||||
|
||||
/**
|
||||
* @brief 注册自定义服务
|
||||
* @tparam T 服务接口类型
|
||||
* @param service 服务实例
|
||||
*/
|
||||
template<typename T>
|
||||
void registerService(SharedPtr<T> service) {
|
||||
ServiceLocator::instance().registerService(service);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取服务
|
||||
* @tparam T 服务接口类型
|
||||
* @return 服务共享指针
|
||||
*/
|
||||
template<typename T>
|
||||
SharedPtr<T> getService() {
|
||||
return ServiceLocator::instance().getService<T>();
|
||||
}
|
||||
|
||||
private:
|
||||
Application() = default;
|
||||
Application();
|
||||
~Application();
|
||||
|
||||
/**
|
||||
* @brief 初始化模块
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
bool initModules();
|
||||
|
||||
/**
|
||||
* @brief 注册核心服务
|
||||
*/
|
||||
void registerCoreServices();
|
||||
|
||||
/**
|
||||
* @brief 主循环
|
||||
*/
|
||||
|
||||
void mainLoop();
|
||||
|
||||
/**
|
||||
* @brief 更新
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* @brief 渲染
|
||||
*/
|
||||
void render();
|
||||
|
||||
IWindow* window_ = nullptr;
|
||||
|
||||
void registerCoreServices();
|
||||
|
||||
bool initialized_ = false;
|
||||
bool running_ = false;
|
||||
bool paused_ = false;
|
||||
bool shouldQuit_ = false;
|
||||
|
||||
|
||||
float deltaTime_ = 0.0f;
|
||||
float totalTime_ = 0.0f;
|
||||
double lastFrameTime_ = 0.0;
|
||||
int frameCount_ = 0;
|
||||
float fpsTimer_ = 0.0f;
|
||||
int currentFps_ = 0;
|
||||
|
||||
AppConfig appConfig_;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @file audio_config.h
|
||||
* @brief 音频模块配置
|
||||
*
|
||||
* 定义音频相关的配置数据结构,由 AudioModule 管理。
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 音频配置数据结构
|
||||
*/
|
||||
struct AudioConfigData {
|
||||
bool enabled = true;
|
||||
int masterVolume = 100;
|
||||
int musicVolume = 100;
|
||||
int sfxVolume = 100;
|
||||
int voiceVolume = 100;
|
||||
int ambientVolume = 100;
|
||||
int frequency = 44100;
|
||||
int channels = 2;
|
||||
int chunkSize = 2048;
|
||||
int maxChannels = 16;
|
||||
bool spatialAudio = false;
|
||||
float listenerPosition[3] = {0.0f, 0.0f, 0.0f};
|
||||
|
||||
/**
|
||||
* @brief 验证音量值是否有效
|
||||
* @param volume 要验证的音量值
|
||||
* @return 如果音量在0-100范围内返回 true
|
||||
*/
|
||||
bool isValidVolume(int volume) const { return volume >= 0 && volume <= 100; }
|
||||
|
||||
/**
|
||||
* @brief 将音量值转换为浮点数
|
||||
* @param volume 音量值(0-100)
|
||||
* @return 浮点数音量值(0.0-1.0)
|
||||
*/
|
||||
float volumeToFloat(int volume) const { return static_cast<float>(volume) / 100.0f; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,200 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/config/app_config.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @file config_loader.h
|
||||
* @brief 配置加载器接口
|
||||
*
|
||||
* 配置加载器只负责加载应用级别的配置(AppConfig)。
|
||||
* 模块配置通过 ModuleRegistry 和各模块的 IModuleConfig 接口加载。
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 配置加载结果
|
||||
*/
|
||||
struct ConfigLoadResult {
|
||||
bool success = false;
|
||||
std::string errorMessage;
|
||||
int errorLine = -1;
|
||||
std::string errorField;
|
||||
|
||||
static ConfigLoadResult ok() { return ConfigLoadResult{true, "", -1, ""}; }
|
||||
static ConfigLoadResult error(const std::string& msg, int line = -1, const std::string& field = "") {
|
||||
return ConfigLoadResult{false, msg, line, field};
|
||||
}
|
||||
|
||||
bool isOk() const { return success; }
|
||||
bool hasError() const { return !success; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 配置保存结果
|
||||
*/
|
||||
struct ConfigSaveResult {
|
||||
bool success = false;
|
||||
std::string errorMessage;
|
||||
|
||||
static ConfigSaveResult ok() { return ConfigSaveResult{true, ""}; }
|
||||
static ConfigSaveResult error(const std::string& msg) {
|
||||
return ConfigSaveResult{false, msg};
|
||||
}
|
||||
|
||||
bool isOk() const { return success; }
|
||||
bool hasError() const { return !success; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 配置加载器抽象接口
|
||||
*/
|
||||
class ConfigLoader {
|
||||
public:
|
||||
virtual ~ConfigLoader() = default;
|
||||
|
||||
/**
|
||||
* @brief 从文件加载应用配置
|
||||
* @param filepath 配置文件路径
|
||||
* @param config 输出的配置对象
|
||||
* @return 加载结果
|
||||
*/
|
||||
virtual ConfigLoadResult load(const std::string& filepath, AppConfig& config) = 0;
|
||||
|
||||
/**
|
||||
* @brief 保存应用配置到文件
|
||||
* @param filepath 配置文件路径
|
||||
* @param config 要保存的配置对象
|
||||
* @return 保存结果
|
||||
*/
|
||||
virtual ConfigSaveResult save(const std::string& filepath, const AppConfig& config) = 0;
|
||||
|
||||
/**
|
||||
* @brief 从字符串加载配置
|
||||
* @param content 配置内容字符串
|
||||
* @param config 输出的配置对象
|
||||
* @return 加载结果
|
||||
*/
|
||||
virtual ConfigLoadResult loadFromString(const std::string& content, AppConfig& config) = 0;
|
||||
|
||||
/**
|
||||
* @brief 将配置序列化为字符串
|
||||
* @param config 配置对象
|
||||
* @return 序列化后的字符串
|
||||
*/
|
||||
virtual std::string saveToString(const AppConfig& config) = 0;
|
||||
|
||||
/**
|
||||
* @brief 从文件加载完整配置(包括模块配置)
|
||||
* @param filepath 配置文件路径
|
||||
* @return 加载结果
|
||||
*/
|
||||
virtual ConfigLoadResult loadWithModules(const std::string& filepath) = 0;
|
||||
|
||||
/**
|
||||
* @brief 保存完整配置(包括模块配置)到文件
|
||||
* @param filepath 配置文件路径
|
||||
* @return 保存结果
|
||||
*/
|
||||
virtual ConfigSaveResult saveWithModules(const std::string& filepath) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取支持的文件扩展名
|
||||
* @return 文件扩展名(不含点号,如 "json")
|
||||
*/
|
||||
virtual const char* extension() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查是否支持指定文件
|
||||
* @param filepath 文件路径
|
||||
* @return 如果支持返回 true
|
||||
*/
|
||||
virtual bool supportsFile(const std::string& filepath) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 克隆加载器实例
|
||||
* @return 新的加载器实例
|
||||
*/
|
||||
virtual UniquePtr<ConfigLoader> clone() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief JSON 配置加载器
|
||||
*/
|
||||
class JsonConfigLoader : public ConfigLoader {
|
||||
public:
|
||||
JsonConfigLoader() = default;
|
||||
~JsonConfigLoader() override = default;
|
||||
|
||||
ConfigLoadResult load(const std::string& filepath, AppConfig& config) override;
|
||||
ConfigSaveResult save(const std::string& filepath, const AppConfig& config) override;
|
||||
ConfigLoadResult loadFromString(const std::string& content, AppConfig& config) override;
|
||||
std::string saveToString(const AppConfig& config) override;
|
||||
ConfigLoadResult loadWithModules(const std::string& filepath) override;
|
||||
ConfigSaveResult saveWithModules(const std::string& filepath) override;
|
||||
const char* extension() const override { return "json"; }
|
||||
bool supportsFile(const std::string& filepath) const override;
|
||||
UniquePtr<ConfigLoader> clone() const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief INI 配置加载器
|
||||
*/
|
||||
class IniConfigLoader : public ConfigLoader {
|
||||
public:
|
||||
IniConfigLoader() = default;
|
||||
~IniConfigLoader() override = default;
|
||||
|
||||
ConfigLoadResult load(const std::string& filepath, AppConfig& config) override;
|
||||
ConfigSaveResult save(const std::string& filepath, const AppConfig& config) override;
|
||||
ConfigLoadResult loadFromString(const std::string& content, AppConfig& config) override;
|
||||
std::string saveToString(const AppConfig& config) override;
|
||||
ConfigLoadResult loadWithModules(const std::string& filepath) override;
|
||||
ConfigSaveResult saveWithModules(const std::string& filepath) override;
|
||||
const char* extension() const override { return "ini"; }
|
||||
bool supportsFile(const std::string& filepath) const override;
|
||||
UniquePtr<ConfigLoader> clone() const override;
|
||||
|
||||
private:
|
||||
std::string sectionKey(const std::string& section, const std::string& key) const;
|
||||
ConfigLoadResult parseInt(const std::string& value, int& result, const std::string& fieldName);
|
||||
ConfigLoadResult parseFloat(const std::string& value, float& result, const std::string& fieldName);
|
||||
ConfigLoadResult parseBool(const std::string& value, bool& result, const std::string& fieldName);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 配置加载器工厂
|
||||
*/
|
||||
class ConfigLoaderFactory {
|
||||
public:
|
||||
/**
|
||||
* @brief 根据文件扩展名创建加载器
|
||||
* @param extension 文件扩展名(不含点号)
|
||||
* @return 配置加载器实例,如果不支持返回 nullptr
|
||||
*/
|
||||
static UniquePtr<ConfigLoader> create(const std::string& extension);
|
||||
|
||||
/**
|
||||
* @brief 根据文件路径创建加载器
|
||||
* @param filepath 文件路径
|
||||
* @return 配置加载器实例,如果不支持返回 nullptr
|
||||
*/
|
||||
static UniquePtr<ConfigLoader> createForFile(const std::string& filepath);
|
||||
|
||||
/**
|
||||
* @brief 检查是否支持指定扩展名
|
||||
* @param extension 文件扩展名
|
||||
* @return 如果支持返回 true
|
||||
*/
|
||||
static bool isExtensionSupported(const std::string& extension);
|
||||
|
||||
/**
|
||||
* @brief 获取所有支持的扩展名
|
||||
* @return 支持的扩展名列表
|
||||
*/
|
||||
static std::vector<std::string> getSupportedExtensions();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,295 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/config/app_config.h>
|
||||
#include <extra2d/config/config_loader.h>
|
||||
#include <extra2d/config/platform_config.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @file config_manager.h
|
||||
* @brief 配置管理器
|
||||
*
|
||||
* 配置管理器只管理应用级别的配置(AppConfig)。
|
||||
* 模块配置通过 ModuleRegistry 管理,各模块实现 IModuleConfig 接口。
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 配置变更事件
|
||||
*/
|
||||
struct ConfigChangeEvent {
|
||||
std::string section;
|
||||
std::string field;
|
||||
std::string oldValue;
|
||||
std::string newValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 配置变更回调类型
|
||||
*/
|
||||
using ConfigChangeCallback = Function<void(const ConfigChangeEvent &)>;
|
||||
|
||||
/**
|
||||
* @brief 配置管理器(单例)
|
||||
*/
|
||||
class ConfigManager {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 配置管理器实例引用
|
||||
*/
|
||||
static ConfigManager &instance();
|
||||
|
||||
/**
|
||||
* @brief 初始化配置管理器
|
||||
* @param configPath 配置文件路径
|
||||
* @return 如果初始化成功返回 true
|
||||
*/
|
||||
bool initialize(const std::string &configPath = "config.json");
|
||||
|
||||
/**
|
||||
* @brief 关闭配置管理器
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
* @return 如果已初始化返回 true
|
||||
*/
|
||||
bool isInitialized() const;
|
||||
|
||||
/**
|
||||
* @brief 加载配置文件
|
||||
* @param filepath 配置文件路径(可选,默认使用初始化时的路径)
|
||||
* @return 加载结果
|
||||
*/
|
||||
ConfigLoadResult loadConfig(const std::string &filepath = "");
|
||||
|
||||
/**
|
||||
* @brief 保存配置到文件
|
||||
* @param filepath 配置文件路径(可选,默认使用初始化时的路径)
|
||||
* @return 保存结果
|
||||
*/
|
||||
ConfigSaveResult saveConfig(const std::string &filepath = "");
|
||||
|
||||
/**
|
||||
* @brief 加载完整配置(包括模块配置)
|
||||
* @param filepath 配置文件路径
|
||||
* @return 加载结果
|
||||
*/
|
||||
ConfigLoadResult loadConfigWithModules(const std::string &filepath = "");
|
||||
|
||||
/**
|
||||
* @brief 保存完整配置(包括模块配置)
|
||||
* @param filepath 配置文件路径
|
||||
* @return 保存结果
|
||||
*/
|
||||
ConfigSaveResult saveConfigWithModules(const std::string &filepath = "");
|
||||
|
||||
/**
|
||||
* @brief 重新加载配置
|
||||
* @return 加载结果
|
||||
*/
|
||||
ConfigLoadResult reload();
|
||||
|
||||
/**
|
||||
* @brief 获取应用配置
|
||||
* @return 应用配置的常量引用
|
||||
*/
|
||||
const AppConfig &appConfig() const;
|
||||
|
||||
/**
|
||||
* @brief 获取可修改的应用配置
|
||||
* @return 应用配置的引用
|
||||
*/
|
||||
AppConfig &appConfig();
|
||||
|
||||
/**
|
||||
* @brief 设置应用配置
|
||||
* @param config 新的配置
|
||||
*/
|
||||
void setAppConfig(const AppConfig &config);
|
||||
|
||||
/**
|
||||
* @brief 获取平台配置
|
||||
* @return 平台配置接口指针
|
||||
*/
|
||||
PlatformConfig *platformConfig();
|
||||
|
||||
/**
|
||||
* @brief 获取平台配置(常量版本)
|
||||
* @return 平台配置接口常量指针
|
||||
*/
|
||||
const PlatformConfig *platformConfig() const;
|
||||
|
||||
/**
|
||||
* @brief 注册配置变更回调
|
||||
* @param callback 回调函数
|
||||
* @return 回调ID,用于取消注册
|
||||
*/
|
||||
int registerChangeCallback(ConfigChangeCallback callback);
|
||||
|
||||
/**
|
||||
* @brief 取消注册配置变更回调
|
||||
* @param callbackId 回调ID
|
||||
*/
|
||||
void unregisterChangeCallback(int callbackId);
|
||||
|
||||
/**
|
||||
* @brief 清除所有变更回调
|
||||
*/
|
||||
void clearChangeCallbacks();
|
||||
|
||||
/**
|
||||
* @brief 设置配置值(字符串)
|
||||
* @param section 配置节
|
||||
* @param key 配置键
|
||||
* @param value 配置值
|
||||
*/
|
||||
void setValue(const std::string §ion, const std::string &key,
|
||||
const std::string &value);
|
||||
|
||||
/**
|
||||
* @brief 设置配置值(整数)
|
||||
* @param section 配置节
|
||||
* @param key 配置键
|
||||
* @param value 配置值
|
||||
*/
|
||||
void setValue(const std::string §ion, const std::string &key, int value);
|
||||
|
||||
/**
|
||||
* @brief 设置配置值(浮点数)
|
||||
* @param section 配置节
|
||||
* @param key 配置键
|
||||
* @param value 配置值
|
||||
*/
|
||||
void setValue(const std::string §ion, const std::string &key,
|
||||
float value);
|
||||
|
||||
/**
|
||||
* @brief 设置配置值(布尔值)
|
||||
* @param section 配置节
|
||||
* @param key 配置键
|
||||
* @param value 配置值
|
||||
*/
|
||||
void setValue(const std::string §ion, const std::string &key, bool value);
|
||||
|
||||
/**
|
||||
* @brief 获取配置值(字符串)
|
||||
* @param section 配置节
|
||||
* @param key 配置键
|
||||
* @param defaultValue 默认值
|
||||
* @return 配置值
|
||||
*/
|
||||
std::string getValue(const std::string §ion, const std::string &key,
|
||||
const std::string &defaultValue = "") const;
|
||||
|
||||
/**
|
||||
* @brief 获取配置值(整数)
|
||||
* @param section 配置节
|
||||
* @param key 配置键
|
||||
* @param defaultValue 默认值
|
||||
* @return 配置值
|
||||
*/
|
||||
int getIntValue(const std::string §ion, const std::string &key,
|
||||
int defaultValue = 0) const;
|
||||
|
||||
/**
|
||||
* @brief 获取配置值(浮点数)
|
||||
* @param section 配置节
|
||||
* @param key 配置键
|
||||
* @param defaultValue 默认值
|
||||
* @return 配置值
|
||||
*/
|
||||
float getFloatValue(const std::string §ion, const std::string &key,
|
||||
float defaultValue = 0.0f) const;
|
||||
|
||||
/**
|
||||
* @brief 获取配置值(布尔值)
|
||||
* @param section 配置节
|
||||
* @param key 配置键
|
||||
* @param defaultValue 默认值
|
||||
* @return 配置值
|
||||
*/
|
||||
bool getBoolValue(const std::string §ion, const std::string &key,
|
||||
bool defaultValue = false) const;
|
||||
|
||||
/**
|
||||
* @brief 重置配置到默认值
|
||||
*/
|
||||
void resetToDefaults();
|
||||
|
||||
/**
|
||||
* @brief 检查配置是否有未保存的更改
|
||||
* @return 如果有未保存的更改返回 true
|
||||
*/
|
||||
bool hasUnsavedChanges() const;
|
||||
|
||||
/**
|
||||
* @brief 标记配置为已修改
|
||||
*/
|
||||
void markModified();
|
||||
|
||||
/**
|
||||
* @brief 清除修改标记
|
||||
*/
|
||||
void clearModified();
|
||||
|
||||
/**
|
||||
* @brief 获取配置文件路径
|
||||
* @return 配置文件路径
|
||||
*/
|
||||
const std::string &configPath() const { return m_configPath; }
|
||||
|
||||
/**
|
||||
* @brief 设置自动保存
|
||||
* @param enabled 是否启用自动保存
|
||||
* @param interval 自动保存间隔(秒)
|
||||
*/
|
||||
void setAutoSave(bool enabled, float interval = 30.0f);
|
||||
|
||||
/**
|
||||
* @brief 检查是否启用自动保存
|
||||
* @return 如果启用自动保存返回 true
|
||||
*/
|
||||
bool isAutoSaveEnabled() const { return m_autoSaveEnabled; }
|
||||
|
||||
/**
|
||||
* @brief 更新配置管理器(用于自动保存)
|
||||
* @param deltaTime 帧时间(秒)
|
||||
*/
|
||||
void update(float deltaTime);
|
||||
|
||||
private:
|
||||
ConfigManager();
|
||||
~ConfigManager();
|
||||
ConfigManager(const ConfigManager &) = delete;
|
||||
ConfigManager &operator=(const ConfigManager &) = delete;
|
||||
|
||||
void notifyChangeCallbacks(const ConfigChangeEvent &event);
|
||||
|
||||
AppConfig m_appConfig;
|
||||
UniquePtr<PlatformConfig> m_platformConfig;
|
||||
UniquePtr<ConfigLoader> m_loader;
|
||||
std::string m_configPath;
|
||||
bool m_initialized = false;
|
||||
bool m_modified = false;
|
||||
mutable std::mutex m_mutex;
|
||||
|
||||
std::unordered_map<int, ConfigChangeCallback> m_changeCallbacks;
|
||||
int m_nextCallbackId = 1;
|
||||
|
||||
std::unordered_map<std::string, std::string> m_rawValues;
|
||||
|
||||
bool m_autoSaveEnabled = false;
|
||||
float m_autoSaveInterval = 30.0f;
|
||||
float m_autoSaveTimer = 0.0f;
|
||||
};
|
||||
|
||||
#define CONFIG_MANAGER ConfigManager::instance()
|
||||
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/config/module_config.h>
|
||||
#include <extra2d/config/module_initializer.h>
|
||||
#include <extra2d/config/app_config.h>
|
||||
#include <extra2d/config/config_manager.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class ConfigModuleConfig : public IModuleConfig {
|
||||
public:
|
||||
std::string configPath;
|
||||
AppConfig appConfig;
|
||||
|
||||
ModuleInfo getModuleInfo() const override {
|
||||
ModuleInfo info;
|
||||
info.id = 0;
|
||||
info.name = "Config";
|
||||
info.version = "1.0.0";
|
||||
info.priority = ModulePriority::Core;
|
||||
info.enabled = true;
|
||||
return info;
|
||||
}
|
||||
|
||||
std::string getConfigSectionName() const override {
|
||||
return "config";
|
||||
}
|
||||
|
||||
bool validate() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void resetToDefaults() override {
|
||||
configPath.clear();
|
||||
appConfig = AppConfig{};
|
||||
}
|
||||
|
||||
bool loadFromJson(const void* jsonData) override;
|
||||
bool saveToJson(void* jsonData) const override;
|
||||
};
|
||||
|
||||
class ConfigModuleInitializer : public IModuleInitializer {
|
||||
public:
|
||||
ConfigModuleInitializer();
|
||||
~ConfigModuleInitializer() override;
|
||||
|
||||
ModuleId getModuleId() const override { return moduleId_; }
|
||||
ModulePriority getPriority() const override { return ModulePriority::Core; }
|
||||
std::vector<ModuleId> getDependencies() const override { return {}; }
|
||||
|
||||
bool initialize(const IModuleConfig* config) override;
|
||||
void shutdown() override;
|
||||
bool isInitialized() const override { return initialized_; }
|
||||
|
||||
void setModuleId(ModuleId id) { moduleId_ = id; }
|
||||
void setAppConfig(const AppConfig& config) { appConfig_ = config; }
|
||||
void setConfigPath(const std::string& path) { configPath_ = path; }
|
||||
|
||||
private:
|
||||
ModuleId moduleId_ = INVALID_MODULE_ID;
|
||||
bool initialized_ = false;
|
||||
AppConfig appConfig_;
|
||||
std::string configPath_;
|
||||
};
|
||||
|
||||
ModuleId get_config_module_id();
|
||||
void register_config_module();
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/config/platform_config.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 模块标识符类型
|
||||
*/
|
||||
using ModuleId = uint32_t;
|
||||
|
||||
/**
|
||||
* @brief 无效模块标识符常量
|
||||
*/
|
||||
constexpr ModuleId INVALID_MODULE_ID = 0;
|
||||
|
||||
/**
|
||||
* @brief 模块优先级枚举
|
||||
* 定义模块的初始化顺序,数值越小越先初始化
|
||||
*/
|
||||
enum class ModulePriority : int {
|
||||
Core = 0, ///< 核心模块(最先初始化)
|
||||
Platform = 100, ///< 平台相关模块
|
||||
Graphics = 200, ///< 图形渲染模块
|
||||
Audio = 300, ///< 音频模块
|
||||
Input = 400, ///< 输入模块
|
||||
Resource = 500, ///< 资源管理模块
|
||||
Game = 1000, ///< 游戏逻辑模块
|
||||
User = 2000 ///< 用户自定义模块
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 模块信息结构体
|
||||
* 包含模块的基本信息
|
||||
*/
|
||||
struct ModuleInfo {
|
||||
ModuleId id = INVALID_MODULE_ID; ///< 模块唯一标识符
|
||||
std::string name; ///< 模块名称
|
||||
std::string version; ///< 模块版本号
|
||||
ModulePriority priority = ModulePriority::User; ///< 模块优先级
|
||||
bool enabled = true; ///< 是否启用
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 模块配置接口
|
||||
* 所有模块配置类必须实现此接口
|
||||
*/
|
||||
class IModuleConfig {
|
||||
public:
|
||||
/**
|
||||
* @brief 虚析构函数
|
||||
*/
|
||||
virtual ~IModuleConfig() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取模块信息
|
||||
* @return 模块信息结构体
|
||||
*/
|
||||
virtual ModuleInfo getModuleInfo() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取配置节名称
|
||||
* 用于配置文件中的节名
|
||||
* @return 配置节名称字符串
|
||||
*/
|
||||
virtual std::string getConfigSectionName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 验证配置有效性
|
||||
* @return 如果配置有效返回 true
|
||||
*/
|
||||
virtual bool validate() const { return true; }
|
||||
|
||||
/**
|
||||
* @brief 应用平台约束
|
||||
* 根据平台特性调整配置
|
||||
* @param platform 目标平台类型
|
||||
*/
|
||||
virtual void applyPlatformConstraints(PlatformType platform) { }
|
||||
|
||||
/**
|
||||
* @brief 重置为默认配置
|
||||
*/
|
||||
virtual void resetToDefaults() = 0;
|
||||
|
||||
/**
|
||||
* @brief 从 JSON 数据加载配置
|
||||
* @param jsonData JSON 数据指针
|
||||
* @return 加载成功返回 true
|
||||
*/
|
||||
virtual bool loadFromJson(const void* jsonData) { return true; }
|
||||
|
||||
/**
|
||||
* @brief 保存配置到 JSON 数据
|
||||
* @param jsonData JSON 数据指针
|
||||
* @return 保存成功返回 true
|
||||
*/
|
||||
virtual bool saveToJson(void* jsonData) const { return true; }
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/config/module_config.h>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 模块初始化器接口
|
||||
* 所有模块初始化器必须实现此接口
|
||||
*/
|
||||
class IModuleInitializer {
|
||||
public:
|
||||
/**
|
||||
* @brief 虚析构函数
|
||||
*/
|
||||
virtual ~IModuleInitializer() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取模块标识符
|
||||
* @return 模块唯一标识符
|
||||
*/
|
||||
virtual ModuleId getModuleId() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取模块优先级
|
||||
* @return 模块优先级
|
||||
*/
|
||||
virtual ModulePriority getPriority() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取模块依赖列表
|
||||
* 返回此模块依赖的其他模块标识符
|
||||
* @return 依赖模块标识符列表
|
||||
*/
|
||||
virtual std::vector<ModuleId> getDependencies() const { return {}; }
|
||||
|
||||
/**
|
||||
* @brief 初始化模块
|
||||
* @param config 模块配置指针
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
virtual bool initialize(const IModuleConfig* config) = 0;
|
||||
|
||||
/**
|
||||
* @brief 关闭模块
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查模块是否已初始化
|
||||
* @return 已初始化返回 true
|
||||
*/
|
||||
virtual bool isInitialized() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 模块初始化器工厂函数类型
|
||||
*/
|
||||
using ModuleInitializerFactory = std::function<UniquePtr<IModuleInitializer>()>;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/config/module_config.h>
|
||||
#include <extra2d/config/module_initializer.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 模块注册表条目结构体
|
||||
* 存储模块的配置和初始化器
|
||||
*/
|
||||
struct ModuleEntry {
|
||||
ModuleId id; ///< 模块标识符
|
||||
UniquePtr<IModuleConfig> config; ///< 模块配置
|
||||
ModuleInitializerFactory initializerFactory;///< 初始化器工厂函数
|
||||
UniquePtr<IModuleInitializer> initializer; ///< 初始化器实例
|
||||
bool initialized = false; ///< 是否已初始化
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 模块注册表类
|
||||
* 单例模式,管理所有模块的注册、查询和初始化顺序
|
||||
*/
|
||||
class ModuleRegistry {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 模块注册表实例引用
|
||||
*/
|
||||
static ModuleRegistry& instance();
|
||||
|
||||
/**
|
||||
* @brief 禁止拷贝构造
|
||||
*/
|
||||
ModuleRegistry(const ModuleRegistry&) = delete;
|
||||
|
||||
/**
|
||||
* @brief 禁止赋值操作
|
||||
*/
|
||||
ModuleRegistry& operator=(const ModuleRegistry&) = delete;
|
||||
|
||||
/**
|
||||
* @brief 注册模块
|
||||
* @param config 模块配置
|
||||
* @param initializerFactory 初始化器工厂函数(可选)
|
||||
* @return 分配的模块标识符
|
||||
*/
|
||||
ModuleId registerModule(
|
||||
UniquePtr<IModuleConfig> config,
|
||||
ModuleInitializerFactory initializerFactory = nullptr
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief 注销模块
|
||||
* @param id 模块标识符
|
||||
* @return 注销成功返回 true
|
||||
*/
|
||||
bool unregisterModule(ModuleId id);
|
||||
|
||||
/**
|
||||
* @brief 获取模块配置
|
||||
* @param id 模块标识符
|
||||
* @return 模块配置指针,不存在返回 nullptr
|
||||
*/
|
||||
IModuleConfig* getModuleConfig(ModuleId id) const;
|
||||
|
||||
/**
|
||||
* @brief 根据名称获取模块配置
|
||||
* @param name 模块名称
|
||||
* @return 模块配置指针,不存在返回 nullptr
|
||||
*/
|
||||
IModuleConfig* getModuleConfigByName(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 获取或创建模块初始化器
|
||||
* @param id 模块标识符
|
||||
* @return 初始化器指针,不存在返回 nullptr
|
||||
*/
|
||||
IModuleInitializer* getInitializer(ModuleId id);
|
||||
|
||||
/**
|
||||
* @brief 获取所有已注册模块标识符
|
||||
* @return 模块标识符列表
|
||||
*/
|
||||
std::vector<ModuleId> getAllModules() const;
|
||||
|
||||
/**
|
||||
* @brief 获取模块初始化顺序
|
||||
* 根据优先级和依赖关系计算初始化顺序
|
||||
* @return 按初始化顺序排列的模块标识符列表
|
||||
*/
|
||||
std::vector<ModuleId> getInitializationOrder() const;
|
||||
|
||||
/**
|
||||
* @brief 检查模块是否存在
|
||||
* @param id 模块标识符
|
||||
* @return 存在返回 true
|
||||
*/
|
||||
bool hasModule(ModuleId id) const;
|
||||
|
||||
/**
|
||||
* @brief 清空所有注册的模块
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 获取已注册模块数量
|
||||
* @return 模块数量
|
||||
*/
|
||||
size_t size() const { return modules_.size(); }
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 私有构造函数
|
||||
*/
|
||||
ModuleRegistry() = default;
|
||||
|
||||
/**
|
||||
* @brief 私有析构函数
|
||||
*/
|
||||
~ModuleRegistry() = default;
|
||||
|
||||
/**
|
||||
* @brief 生成新的模块标识符
|
||||
* @return 新的模块标识符
|
||||
*/
|
||||
ModuleId generateId();
|
||||
|
||||
std::unordered_map<ModuleId, ModuleEntry> modules_; ///< 模块注册表
|
||||
std::unordered_map<std::string, ModuleId> nameToId_; ///< 名称到标识符映射
|
||||
mutable std::mutex mutex_; ///< 线程安全互斥锁
|
||||
ModuleId nextId_ = 1; ///< 下一个可用标识符
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 模块注册宏
|
||||
* 在全局作用域使用此宏注册模块
|
||||
*
|
||||
* @example
|
||||
* E2D_REGISTER_MODULE(MyModuleConfig, MyModuleInitializer)
|
||||
*/
|
||||
#define E2D_REGISTER_MODULE(ConfigClass, InitializerClass) \
|
||||
namespace { \
|
||||
static const ::extra2d::ModuleId E2D_ANONYMOUS_VAR(module_id_) = \
|
||||
::extra2d::ModuleRegistry::instance().registerModule( \
|
||||
::extra2d::makeUnique<ConfigClass>(), \
|
||||
[]() -> ::extra2d::UniquePtr<::extra2d::IModuleInitializer> { \
|
||||
return ::extra2d::makeUnique<InitializerClass>(); \
|
||||
} \
|
||||
); \
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <typeindex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Application;
|
||||
|
||||
/**
|
||||
* @brief 模块基类
|
||||
* 所有模块必须继承此类
|
||||
*/
|
||||
class Module {
|
||||
public:
|
||||
virtual ~Module() = default;
|
||||
|
||||
/**
|
||||
* @brief 初始化模块
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
virtual bool init() = 0;
|
||||
|
||||
/**
|
||||
* @brief 关闭模块
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查模块是否已初始化
|
||||
* @return 已初始化返回 true
|
||||
*/
|
||||
virtual bool ok() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取模块名称
|
||||
* @return 模块名称
|
||||
*/
|
||||
virtual const char* name() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取模块优先级(数值越小越优先)
|
||||
* @return 优先级值
|
||||
*/
|
||||
virtual int priority() const { return 100; }
|
||||
|
||||
/**
|
||||
* @brief 获取模块依赖列表
|
||||
* @return 依赖模块类型列表
|
||||
*/
|
||||
virtual std::vector<std::type_index> deps() const { return {}; }
|
||||
|
||||
/**
|
||||
* @brief 设置所属Application
|
||||
* @param app Application指针
|
||||
*/
|
||||
void setApp(class Application* app) { app_ = app; }
|
||||
|
||||
/**
|
||||
* @brief 获取Application
|
||||
* @return Application指针
|
||||
*/
|
||||
class Application* app() const { return app_; }
|
||||
|
||||
protected:
|
||||
class Application* app_ = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 模块工厂函数类型
|
||||
*/
|
||||
using ModuleFactory = std::function<UniquePtr<Module>()>;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <unordered_map>
|
||||
#include <typeindex>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Application;
|
||||
|
||||
/**
|
||||
* @brief 模块注册表
|
||||
* 管理模块的注册、拓扑排序和生命周期
|
||||
*/
|
||||
class Registry {
|
||||
public:
|
||||
static Registry& instance();
|
||||
|
||||
Registry(const Registry&) = delete;
|
||||
Registry& operator=(const Registry&) = delete;
|
||||
|
||||
/**
|
||||
* @brief 注册模块
|
||||
* @tparam T 模块类型
|
||||
* @tparam Args 构造函数参数类型
|
||||
* @param args 构造函数参数
|
||||
* @return 模块指针
|
||||
*/
|
||||
template<typename T, typename... Args>
|
||||
T* use(Args&&... args) {
|
||||
static_assert(std::is_base_of_v<Module, T>, "T must derive from Module");
|
||||
|
||||
auto typeIdx = std::type_index(typeid(T));
|
||||
if (modules_.count(typeIdx)) {
|
||||
return static_cast<T*>(modules_[typeIdx].get());
|
||||
}
|
||||
|
||||
auto module = makeUnique<T>(std::forward<Args>(args)...);
|
||||
T* ptr = module.get();
|
||||
module->setApp(app_);
|
||||
modules_[typeIdx] = std::move(module);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取模块
|
||||
* @tparam T 模块类型
|
||||
* @return 模块指针,不存在返回 nullptr
|
||||
*/
|
||||
template<typename T>
|
||||
T* get() const {
|
||||
auto typeIdx = std::type_index(typeid(T));
|
||||
auto it = modules_.find(typeIdx);
|
||||
if (it != modules_.end()) {
|
||||
return static_cast<T*>(it->second.get());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取模块(基类版本)
|
||||
* @param typeIdx 类型索引
|
||||
* @return 模块指针
|
||||
*/
|
||||
Module* get(std::type_index typeIdx) const {
|
||||
auto it = modules_.find(typeIdx);
|
||||
if (it != modules_.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置Application
|
||||
*/
|
||||
void setApp(Application* app) { app_ = app; }
|
||||
|
||||
/**
|
||||
* @brief 初始化所有模块(按优先级拓扑排序)
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief 关闭所有模块
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 清空所有模块
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 获取模块数量
|
||||
*/
|
||||
size_t size() const { return modules_.size(); }
|
||||
|
||||
private:
|
||||
Registry() = default;
|
||||
~Registry() = default;
|
||||
|
||||
/**
|
||||
* @brief 拓扑排序模块
|
||||
* @return 排序后的模块列表
|
||||
*/
|
||||
std::vector<Module*> topologicalSort();
|
||||
|
||||
std::unordered_map<std::type_index, UniquePtr<Module>> modules_;
|
||||
Application* app_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @file debug_config.h
|
||||
* @brief 调试模块配置
|
||||
*
|
||||
* 定义调试相关的配置数据结构,由 DebugModule 管理。
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 调试配置数据结构
|
||||
*/
|
||||
struct DebugConfigData {
|
||||
bool enabled = false;
|
||||
bool showFPS = false;
|
||||
bool showMemoryUsage = false;
|
||||
bool showRenderStats = false;
|
||||
bool showColliders = false;
|
||||
bool showGrid = false;
|
||||
bool logToFile = false;
|
||||
bool logToConsole = true;
|
||||
int logLevel = 2;
|
||||
bool breakOnAssert = true;
|
||||
bool enableProfiling = false;
|
||||
std::string logFilePath;
|
||||
std::vector<std::string> debugFlags;
|
||||
|
||||
/**
|
||||
* @brief 检查是否存在指定的调试标志
|
||||
* @param flag 要检查的标志名称
|
||||
* @return 如果存在返回 true
|
||||
*/
|
||||
bool hasDebugFlag(const std::string& flag) const;
|
||||
|
||||
/**
|
||||
* @brief 添加调试标志
|
||||
* @param flag 要添加的标志名称
|
||||
*/
|
||||
void addDebugFlag(const std::string& flag);
|
||||
|
||||
/**
|
||||
* @brief 移除调试标志
|
||||
* @param flag 要移除的标志名称
|
||||
*/
|
||||
void removeDebugFlag(const std::string& flag);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -7,14 +7,11 @@
|
|||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/core/registry.h>
|
||||
|
||||
// Config
|
||||
#include <extra2d/config/app_config.h>
|
||||
#include <extra2d/config/config_loader.h>
|
||||
#include <extra2d/config/config_manager.h>
|
||||
#include <extra2d/config/module_config.h>
|
||||
#include <extra2d/config/module_initializer.h>
|
||||
#include <extra2d/config/module_registry.h>
|
||||
#include <extra2d/config/platform_config.h>
|
||||
#include <extra2d/config/platform_detector.h>
|
||||
|
||||
|
|
@ -23,21 +20,20 @@
|
|||
#include <extra2d/platform/iwindow.h>
|
||||
#include <extra2d/platform/keys.h>
|
||||
#include <extra2d/platform/input_module.h>
|
||||
#include <extra2d/platform/platform_module.h>
|
||||
#include <extra2d/platform/backend_factory.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
|
||||
// Graphics
|
||||
#include <extra2d/graphics/camera.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/shader_manager.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
|
||||
#include <extra2d/graphics/render_target.h>
|
||||
#include <extra2d/graphics/viewport_adapter.h>
|
||||
#include <extra2d/graphics/vram_manager.h>
|
||||
|
||||
#include <extra2d/graphics/texture_pool.h>
|
||||
#include <extra2d/graphics/camera/camera.h>
|
||||
#include <extra2d/graphics/texture/font.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/graphics/core/render_module.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/graphics/core/render_target.h>
|
||||
#include <extra2d/graphics/camera/viewport_adapter.h>
|
||||
#include <extra2d/graphics/memory/vram_manager.h>
|
||||
#include <extra2d/graphics/texture/texture_pool.h>
|
||||
|
||||
// Scene
|
||||
#include <extra2d/scene/node.h>
|
||||
|
|
@ -52,7 +48,6 @@
|
|||
#include <extra2d/event/event_queue.h>
|
||||
|
||||
// Utils
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/utils/random.h>
|
||||
#include <extra2d/utils/timer.h>
|
||||
|
||||
|
|
@ -61,6 +56,7 @@
|
|||
#include <extra2d/services/scene_service.h>
|
||||
#include <extra2d/services/timer_service.h>
|
||||
#include <extra2d/services/camera_service.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
// Application
|
||||
#include <extra2d/app/application.h>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <cstdint>
|
||||
#include <variant>
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <typeindex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 渲染模块
|
||||
* 管理渲染后端
|
||||
*/
|
||||
class RenderModule : public Module {
|
||||
public:
|
||||
/**
|
||||
* @brief 配置结构
|
||||
*/
|
||||
struct Cfg {
|
||||
BackendType backend;
|
||||
int targetFPS;
|
||||
bool vsync;
|
||||
int multisamples;
|
||||
int priority;
|
||||
|
||||
Cfg()
|
||||
: backend(BackendType::OpenGL)
|
||||
, targetFPS(60)
|
||||
, vsync(true)
|
||||
, multisamples(0)
|
||||
, priority(10)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param cfg 配置
|
||||
*/
|
||||
explicit RenderModule(const Cfg& cfg = Cfg{});
|
||||
|
||||
/**
|
||||
* @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:
|
||||
Cfg cfg_;
|
||||
UniquePtr<RenderBackend> renderer_;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <mutex>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -3,9 +3,9 @@
|
|||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/graphics/texture/font.h>
|
||||
#include <extra2d/graphics/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <memory>
|
||||
#include <stb/stb_rect_pack.h>
|
||||
#include <stb/stb_truetype.h>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/opengl/gl_sprite_batch.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/shader_interface.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/graphics/shader/shader_interface.h>
|
||||
|
||||
#include <array>
|
||||
#include <glad/glad.h>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/graphics/shader_interface.h>
|
||||
#include <extra2d/graphics/shader/shader_interface.h>
|
||||
#include <glad/glad.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/opengl/gl_shader.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <vector>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/alpha_mask.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/graphics/texture/alpha_mask.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @file render_config.h
|
||||
* @brief 渲染模块配置
|
||||
*
|
||||
* 定义渲染相关的配置数据结构,由 RenderModule 管理。
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 渲染配置数据结构
|
||||
*/
|
||||
struct RenderConfigData {
|
||||
BackendType backend = BackendType::OpenGL;
|
||||
int targetFPS = 60;
|
||||
bool vsync = true;
|
||||
bool tripleBuffering = false;
|
||||
int multisamples = 0;
|
||||
bool sRGBFramebuffer = false;
|
||||
Color clearColor{0.0f, 0.0f, 0.0f, 1.0f};
|
||||
int maxTextureSize = 0;
|
||||
int textureAnisotropy = 1;
|
||||
bool wireframeMode = false;
|
||||
bool depthTest = false;
|
||||
bool blending = true;
|
||||
bool dithering = false;
|
||||
int spriteBatchSize = 1000;
|
||||
int maxRenderTargets = 1;
|
||||
bool allowShaderHotReload = false;
|
||||
std::string shaderCachePath;
|
||||
|
||||
/**
|
||||
* @brief 检查是否启用多重采样
|
||||
* @return 如果多重采样数大于0返回 true
|
||||
*/
|
||||
bool isMultisampleEnabled() const { return multisamples > 0; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否限制帧率
|
||||
* @return 如果设置了目标帧率返回 true
|
||||
*/
|
||||
bool isFPSCapped() const { return targetFPS > 0; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/config/module_config.h>
|
||||
#include <extra2d/config/module_initializer.h>
|
||||
#include <extra2d/graphics/render_config.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/core/types.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class RenderModuleConfig : public IModuleConfig {
|
||||
public:
|
||||
BackendType backend = BackendType::OpenGL;
|
||||
bool vsync = true;
|
||||
int targetFPS = 60;
|
||||
int multisamples = 0;
|
||||
bool sRGBFramebuffer = false;
|
||||
int spriteBatchSize = 1000;
|
||||
|
||||
ModuleInfo getModuleInfo() const override {
|
||||
ModuleInfo info;
|
||||
info.name = "Render";
|
||||
info.version = "1.0.0";
|
||||
info.priority = ModulePriority::Graphics;
|
||||
info.enabled = true;
|
||||
return info;
|
||||
}
|
||||
|
||||
std::string getConfigSectionName() const override { return "render"; }
|
||||
|
||||
bool validate() const override;
|
||||
void applyPlatformConstraints(PlatformType platform) override;
|
||||
void resetToDefaults() override;
|
||||
bool loadFromJson(const void* jsonData) override;
|
||||
bool saveToJson(void* jsonData) const override;
|
||||
};
|
||||
|
||||
class RenderModuleInitializer : public IModuleInitializer {
|
||||
public:
|
||||
RenderModuleInitializer();
|
||||
~RenderModuleInitializer() override;
|
||||
|
||||
ModuleId getModuleId() const override { return moduleId_; }
|
||||
ModulePriority getPriority() const override { return ModulePriority::Graphics; }
|
||||
std::vector<ModuleId> getDependencies() const override;
|
||||
|
||||
bool initialize(const IModuleConfig* config) override;
|
||||
void shutdown() override;
|
||||
bool isInitialized() const override { return initialized_; }
|
||||
|
||||
void setModuleId(ModuleId id) { moduleId_ = id; }
|
||||
void setWindow(IWindow* window) { window_ = window; }
|
||||
|
||||
RenderBackend* getRenderer() const { return renderer_.get(); }
|
||||
|
||||
private:
|
||||
ModuleId moduleId_ = INVALID_MODULE_ID;
|
||||
IWindow* window_ = nullptr;
|
||||
UniquePtr<RenderBackend> renderer_;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
ModuleId get_render_module_id();
|
||||
void register_render_module();
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/config/platform_detector.h>
|
||||
#include <extra2d/graphics/shader_cache.h>
|
||||
#include <extra2d/graphics/shader_hot_reloader.h>
|
||||
#include <extra2d/graphics/shader_interface.h>
|
||||
#include <extra2d/graphics/shader_loader.h>
|
||||
#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>
|
||||
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/shader_interface.h>
|
||||
#include <extra2d/graphics/shader/shader_interface.h>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/graphics/opengl/gl_texture.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @file input_config.h
|
||||
* @brief 输入模块配置
|
||||
*
|
||||
* 定义输入相关的配置数据结构,由 InputModule 管理。
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 输入配置数据结构
|
||||
*/
|
||||
struct InputConfigData {
|
||||
bool enabled = true;
|
||||
bool rawMouseInput = false;
|
||||
float mouseSensitivity = 1.0f;
|
||||
bool invertMouseY = false;
|
||||
bool invertMouseX = false;
|
||||
float deadzone = 0.15f;
|
||||
float triggerThreshold = 0.5f;
|
||||
bool enableVibration = true;
|
||||
int maxGamepads = 4;
|
||||
bool autoConnectGamepads = true;
|
||||
std::string gamepadMappingFile;
|
||||
|
||||
/**
|
||||
* @brief 验证死区值是否有效
|
||||
* @return 如果死区值在0-1范围内返回 true
|
||||
*/
|
||||
bool isDeadzoneValid() const { return deadzone >= 0.0f && deadzone <= 1.0f; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -10,16 +10,6 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 平台模块配置
|
||||
*/
|
||||
struct PlatformModuleConfig {
|
||||
std::string backend = "sdl2";
|
||||
bool gamepad = true;
|
||||
bool touch = true;
|
||||
float deadzone = 0.15f;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 平台后端工厂
|
||||
* 用于注册和创建平台后端
|
||||
|
|
@ -1,129 +1,77 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/config/module_config.h>
|
||||
#include <extra2d/config/module_initializer.h>
|
||||
#include <extra2d/input/input_config.h>
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/platform/iinput.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <typeindex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @file input_module.h
|
||||
* @brief 输入模块
|
||||
*
|
||||
* 输入模块管理键盘、鼠标、手柄和触摸输入。
|
||||
* 通过事件系统分发输入事件。
|
||||
* 管理输入设备
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 输入模块配置
|
||||
* 实现 IModuleConfig 接口
|
||||
*/
|
||||
class InputModuleConfig : public IModuleConfig {
|
||||
class InputModule : public Module {
|
||||
public:
|
||||
InputConfigData inputConfig;
|
||||
|
||||
/**
|
||||
* @brief 获取模块信息
|
||||
* @return 模块信息结构体
|
||||
* @brief 配置结构
|
||||
*/
|
||||
ModuleInfo getModuleInfo() const override {
|
||||
ModuleInfo info;
|
||||
info.id = 0;
|
||||
info.name = "Input";
|
||||
info.version = "1.0.0";
|
||||
info.priority = ModulePriority::Input;
|
||||
info.enabled = true;
|
||||
return info;
|
||||
}
|
||||
struct Cfg {
|
||||
float deadzone;
|
||||
float mouseSensitivity;
|
||||
bool enableVibration;
|
||||
int maxGamepads;
|
||||
int priority;
|
||||
|
||||
Cfg()
|
||||
: deadzone(0.15f)
|
||||
, mouseSensitivity(1.0f)
|
||||
, enableVibration(true)
|
||||
, maxGamepads(4)
|
||||
, priority(20)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 获取配置节名称
|
||||
* @return 配置节名称字符串
|
||||
* @brief 构造函数
|
||||
* @param cfg 配置
|
||||
*/
|
||||
std::string getConfigSectionName() const override { return "input"; }
|
||||
explicit InputModule(const Cfg& cfg = Cfg{});
|
||||
|
||||
/**
|
||||
* @brief 验证配置有效性
|
||||
* @return 如果配置有效返回 true
|
||||
* @brief 析构函数
|
||||
*/
|
||||
bool validate() const override;
|
||||
~InputModule() override;
|
||||
|
||||
/**
|
||||
* @brief 应用平台约束
|
||||
* @param platform 目标平台类型
|
||||
*/
|
||||
void applyPlatformConstraints(PlatformType platform) override;
|
||||
|
||||
/**
|
||||
* @brief 重置为默认配置
|
||||
*/
|
||||
void resetToDefaults() override {
|
||||
inputConfig = InputConfigData{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从 JSON 数据加载配置
|
||||
* @param jsonData JSON 数据指针
|
||||
* @return 加载成功返回 true
|
||||
*/
|
||||
bool loadFromJson(const void* jsonData) override;
|
||||
|
||||
/**
|
||||
* @brief 保存配置到 JSON 数据
|
||||
* @param jsonData JSON 数据指针
|
||||
* @return 保存成功返回 true
|
||||
*/
|
||||
bool saveToJson(void* jsonData) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 输入模块初始化器
|
||||
* 实现 IModuleInitializer 接口
|
||||
*/
|
||||
class InputModuleInitializer : public IModuleInitializer {
|
||||
public:
|
||||
InputModuleInitializer();
|
||||
~InputModuleInitializer() override;
|
||||
|
||||
ModuleId getModuleId() const override { return moduleId_; }
|
||||
ModulePriority getPriority() const override { return ModulePriority::Input; }
|
||||
std::vector<ModuleId> getDependencies() const override;
|
||||
bool initialize(const IModuleConfig* config) override;
|
||||
bool init() override;
|
||||
void shutdown() override;
|
||||
bool isInitialized() const override { return initialized_; }
|
||||
bool ok() const override { return initialized_; }
|
||||
const char* name() const override { return "input"; }
|
||||
int priority() const override { return cfg_.priority; }
|
||||
|
||||
void setModuleId(ModuleId id) { moduleId_ = id; }
|
||||
/**
|
||||
* @brief 获取依赖
|
||||
* @return 依赖模块类型列表
|
||||
*/
|
||||
std::vector<std::type_index> deps() const override {
|
||||
return {std::type_index(typeid(WindowModule))};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取输入接口
|
||||
* @return 输入接口指针
|
||||
*/
|
||||
IInput* getInput() const { return input_; }
|
||||
IInput* input() const { return input_; }
|
||||
|
||||
/**
|
||||
* @brief 更新输入状态
|
||||
* 每帧调用,更新输入状态并分发事件
|
||||
*/
|
||||
void update();
|
||||
|
||||
private:
|
||||
ModuleId moduleId_ = INVALID_MODULE_ID;
|
||||
Cfg cfg_;
|
||||
IInput* input_ = nullptr;
|
||||
bool initialized_ = false;
|
||||
InputConfigData config_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 获取输入模块标识符
|
||||
* @return 输入模块标识符
|
||||
*/
|
||||
ModuleId get_input_module_id();
|
||||
|
||||
/**
|
||||
* @brief 注册输入模块
|
||||
*/
|
||||
void register_input_module();
|
||||
|
||||
}
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/config/module_config.h>
|
||||
#include <extra2d/config/module_initializer.h>
|
||||
#include <extra2d/config/platform_config.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class PlatformModuleConfig : public IModuleConfig {
|
||||
public:
|
||||
PlatformType targetPlatform = PlatformType::Auto;
|
||||
|
||||
ModuleInfo getModuleInfo() const override {
|
||||
ModuleInfo info;
|
||||
info.id = 0;
|
||||
info.name = "Platform";
|
||||
info.version = "1.0.0";
|
||||
info.priority = ModulePriority::Core;
|
||||
info.enabled = true;
|
||||
return info;
|
||||
}
|
||||
|
||||
std::string getConfigSectionName() const override {
|
||||
return "platform";
|
||||
}
|
||||
|
||||
bool validate() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void resetToDefaults() override {
|
||||
targetPlatform = PlatformType::Auto;
|
||||
}
|
||||
|
||||
bool loadFromJson(const void* jsonData) override;
|
||||
bool saveToJson(void* jsonData) const override;
|
||||
};
|
||||
|
||||
class PlatformModuleInitializer : public IModuleInitializer {
|
||||
public:
|
||||
PlatformModuleInitializer();
|
||||
~PlatformModuleInitializer() override;
|
||||
|
||||
ModuleId getModuleId() const override { return moduleId_; }
|
||||
ModulePriority getPriority() const override { return ModulePriority::Core; }
|
||||
std::vector<ModuleId> getDependencies() const override { return {}; }
|
||||
|
||||
bool initialize(const IModuleConfig* config) override;
|
||||
void shutdown() override;
|
||||
bool isInitialized() const override { return initialized_; }
|
||||
|
||||
void setModuleId(ModuleId id) { moduleId_ = id; }
|
||||
void setPlatform(PlatformType platform) { targetPlatform_ = platform; }
|
||||
|
||||
PlatformType getPlatform() const { return resolvedPlatform_; }
|
||||
PlatformConfig* getPlatformConfig() const { return platformConfig_.get(); }
|
||||
|
||||
private:
|
||||
bool initSwitch();
|
||||
void shutdownSwitch();
|
||||
|
||||
ModuleId moduleId_ = INVALID_MODULE_ID;
|
||||
bool initialized_ = false;
|
||||
PlatformType targetPlatform_ = PlatformType::Auto;
|
||||
PlatformType resolvedPlatform_ = PlatformType::Windows;
|
||||
UniquePtr<PlatformConfig> platformConfig_;
|
||||
};
|
||||
|
||||
ModuleId get_platform_module_id();
|
||||
void register_platform_module();
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,195 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/config/module_config.h>
|
||||
#include <extra2d/config/module_initializer.h>
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/platform/window_config.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @file window_module.h
|
||||
* @brief 窗口模块
|
||||
*
|
||||
* 窗口模块使用 SDL2 作为唯一后端,支持以下平台:
|
||||
* - Windows
|
||||
* - Linux
|
||||
* - macOS
|
||||
* - Nintendo Switch
|
||||
* 管理窗口创建和生命周期
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 窗口模块配置
|
||||
* 实现 IModuleConfig 接口
|
||||
*/
|
||||
class WindowModuleConfig : public IModuleConfig {
|
||||
class WindowModule : public Module {
|
||||
public:
|
||||
WindowConfigData windowConfig;
|
||||
|
||||
/**
|
||||
* @brief 获取模块信息
|
||||
* @return 模块信息结构体
|
||||
* @brief 配置结构
|
||||
*/
|
||||
ModuleInfo getModuleInfo() const override {
|
||||
ModuleInfo info;
|
||||
info.id = 0;
|
||||
info.name = "Window";
|
||||
info.version = "1.0.0";
|
||||
info.priority = ModulePriority::Core;
|
||||
info.enabled = true;
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取配置节名称
|
||||
* @return 配置节名称字符串
|
||||
*/
|
||||
std::string getConfigSectionName() const override {
|
||||
return "window";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 验证配置有效性
|
||||
* @return 如果配置有效返回 true
|
||||
*/
|
||||
bool validate() const override {
|
||||
return windowConfig.width > 0 && windowConfig.height > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 应用平台约束
|
||||
* @param platform 目标平台类型
|
||||
*/
|
||||
void applyPlatformConstraints(PlatformType platform) override;
|
||||
|
||||
/**
|
||||
* @brief 重置为默认配置
|
||||
*/
|
||||
void resetToDefaults() override {
|
||||
windowConfig = WindowConfigData{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从 JSON 数据加载配置
|
||||
* @param jsonData JSON 数据指针
|
||||
* @return 加载成功返回 true
|
||||
*/
|
||||
bool loadFromJson(const void* jsonData) override;
|
||||
|
||||
/**
|
||||
* @brief 保存配置到 JSON 数据
|
||||
* @param jsonData JSON 数据指针
|
||||
* @return 保存成功返回 true
|
||||
*/
|
||||
bool saveToJson(void* jsonData) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 窗口模块初始化器
|
||||
* 实现 IModuleInitializer 接口
|
||||
*/
|
||||
class WindowModuleInitializer : public IModuleInitializer {
|
||||
public:
|
||||
struct Cfg {
|
||||
std::string title;
|
||||
int w;
|
||||
int h;
|
||||
WindowMode mode;
|
||||
bool vsync;
|
||||
int priority;
|
||||
std::string backend;
|
||||
|
||||
Cfg()
|
||||
: title("Extra2D")
|
||||
, w(1280)
|
||||
, h(720)
|
||||
, mode(WindowMode::Windowed)
|
||||
, vsync(true)
|
||||
, priority(0)
|
||||
, backend("sdl2") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param cfg 配置
|
||||
*/
|
||||
WindowModuleInitializer();
|
||||
|
||||
explicit WindowModule(const Cfg& cfg = Cfg());
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~WindowModuleInitializer() override;
|
||||
|
||||
/**
|
||||
* @brief 获取模块标识符
|
||||
* @return 模块唯一标识符
|
||||
*/
|
||||
ModuleId getModuleId() const override { return moduleId_; }
|
||||
|
||||
/**
|
||||
* @brief 获取模块优先级
|
||||
* @return 模块优先级
|
||||
*/
|
||||
ModulePriority getPriority() const override { return ModulePriority::Core; }
|
||||
|
||||
/**
|
||||
* @brief 获取模块依赖列表
|
||||
* @return 依赖模块标识符列表(窗口模块无依赖)
|
||||
*/
|
||||
std::vector<ModuleId> getDependencies() const override { return {}; }
|
||||
|
||||
/**
|
||||
* @brief 初始化模块
|
||||
* @param config 模块配置指针
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
bool initialize(const IModuleConfig* config) override;
|
||||
|
||||
/**
|
||||
* @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 已初始化返回 true
|
||||
* @brief 获取窗口
|
||||
* @return 窗口指针
|
||||
*/
|
||||
bool isInitialized() const override { return initialized_; }
|
||||
|
||||
/**
|
||||
* @brief 设置模块标识符
|
||||
* @param id 模块标识符
|
||||
*/
|
||||
void setModuleId(ModuleId id) { moduleId_ = id; }
|
||||
|
||||
/**
|
||||
* @brief 设置窗口配置
|
||||
* @param config 窗口配置数据
|
||||
*/
|
||||
void setWindowConfig(const WindowConfigData& config) { windowConfig_ = config; }
|
||||
|
||||
/**
|
||||
* @brief 获取窗口接口
|
||||
* @return 窗口接口指针
|
||||
*/
|
||||
IWindow* getWindow() const { return window_.get(); }
|
||||
IWindow* win() const { return win_.get(); }
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 初始化 SDL2 后端
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
bool initSDL2();
|
||||
|
||||
/**
|
||||
* @brief 关闭 SDL2 后端
|
||||
*/
|
||||
void shutdownSDL2();
|
||||
|
||||
/**
|
||||
* @brief 创建窗口
|
||||
* @param config 窗口配置数据
|
||||
* @return 创建成功返回 true
|
||||
*/
|
||||
bool createWindow(const WindowConfigData& config);
|
||||
|
||||
ModuleId moduleId_ = INVALID_MODULE_ID;
|
||||
Cfg cfg_;
|
||||
UniquePtr<IWindow> win_;
|
||||
bool initialized_ = false;
|
||||
bool sdl2Initialized_ = false;
|
||||
WindowConfigData windowConfig_;
|
||||
UniquePtr<IWindow> window_;
|
||||
bool sdlInited_ = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 获取窗口模块标识符
|
||||
* @return 窗口模块标识符
|
||||
*/
|
||||
ModuleId get_window_module_id();
|
||||
|
||||
/**
|
||||
* @brief 注册窗口模块
|
||||
*/
|
||||
void register_window_module();
|
||||
|
||||
}
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @file resource_config.h
|
||||
* @brief 资源模块配置
|
||||
*
|
||||
* 定义资源相关的配置数据结构,由 ResourceModule 管理。
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 资源配置数据结构
|
||||
*/
|
||||
struct ResourceConfigData {
|
||||
std::string assetRootPath = "assets";
|
||||
std::string cachePath = "cache";
|
||||
std::string savePath = "saves";
|
||||
std::string configPath = "config";
|
||||
std::string logPath = "logs";
|
||||
bool useAssetCache = true;
|
||||
int maxCacheSize = 512;
|
||||
bool hotReloadEnabled = false;
|
||||
float hotReloadInterval = 1.0f;
|
||||
bool compressTextures = false;
|
||||
bool preloadCommonAssets = true;
|
||||
std::vector<std::string> searchPaths;
|
||||
|
||||
/**
|
||||
* @brief 添加资源搜索路径
|
||||
* @param path 要添加的搜索路径
|
||||
*/
|
||||
void addSearchPath(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 移除资源搜索路径
|
||||
* @param path 要移除的搜索路径
|
||||
*/
|
||||
void removeSearchPath(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 检查是否存在指定的搜索路径
|
||||
* @param path 要检查的路径
|
||||
* @return 如果存在返回 true
|
||||
*/
|
||||
bool hasSearchPath(const std::string& path) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/event/event_dispatcher.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/graphics/camera.h>
|
||||
#include <extra2d/graphics/camera/camera.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <vector>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ public:
|
|||
protected:
|
||||
void onTransitionStart() override;
|
||||
void renderContent(RenderBackend &renderer) override;
|
||||
void updateTransition(float dt);
|
||||
|
||||
private:
|
||||
int divisions_;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,12 @@ protected:
|
|||
*/
|
||||
void onTransitionStart() override;
|
||||
|
||||
/**
|
||||
* @brief 更新过渡进度
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*/
|
||||
void updateTransition(float dt) override;
|
||||
|
||||
/**
|
||||
* @brief 渲染内容
|
||||
* 根据进度控制新旧场景的显示
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public:
|
|||
protected:
|
||||
void onTransitionStart() override;
|
||||
void renderContent(RenderBackend &renderer) override;
|
||||
void updateTransition(float dt);
|
||||
|
||||
private:
|
||||
Axis axis_;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ public:
|
|||
protected:
|
||||
void onTransitionStart() override;
|
||||
void renderContent(RenderBackend &renderer) override;
|
||||
void updateTransition(float dt);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -80,11 +80,22 @@ public:
|
|||
*/
|
||||
bool isFinished() const { return isFinished_; }
|
||||
|
||||
/**
|
||||
* @brief 是否已取消
|
||||
*/
|
||||
bool isCancelled() const { return isCancelled_; }
|
||||
|
||||
/**
|
||||
* @brief 完成过渡,通知 SceneManager 切换到目标场景
|
||||
*/
|
||||
void finish();
|
||||
|
||||
/**
|
||||
* @brief 取消过渡
|
||||
* @param immediate 是否立即完成(false则回滚到原场景)
|
||||
*/
|
||||
void cancel(bool immediate = false);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 渲染 - 在 TransitionScene 上渲染新旧两个子场景
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -113,15 +124,25 @@ protected:
|
|||
*/
|
||||
virtual void drawInScene(RenderBackend &renderer);
|
||||
|
||||
/**
|
||||
* @brief 更新过渡进度(子类重写此方法更新动画)
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*/
|
||||
virtual void updateTransition(float dt);
|
||||
|
||||
float duration_;
|
||||
float elapsed_ = 0.0f;
|
||||
float progress_ = 0.0f;
|
||||
bool isFinished_ = false;
|
||||
bool isCancelled_ = false;
|
||||
|
||||
Ptr<Scene> inScene_; // 要进入的场景
|
||||
Ptr<Scene> outScene_; // 要退出的场景
|
||||
|
||||
FinishCallback finishCallback_;
|
||||
FinishCallback cancelCallback_; // 取消回调
|
||||
|
||||
friend class SceneManager;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ public:
|
|||
protected:
|
||||
void onTransitionStart() override;
|
||||
void renderContent(RenderBackend &renderer) override;
|
||||
void updateTransition(float dt);
|
||||
|
||||
private:
|
||||
TransitionDirection direction_;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/service_interface.h>
|
||||
#include <extra2d/graphics/camera.h>
|
||||
#include <extra2d/graphics/viewport_adapter.h>
|
||||
#include <extra2d/graphics/camera/camera.h>
|
||||
#include <extra2d/graphics/camera/viewport_adapter.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,228 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/service_interface.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 日志级别枚举
|
||||
*/
|
||||
enum class LogLevel {
|
||||
Trace = 0,
|
||||
Debug = 1,
|
||||
Info = 2,
|
||||
Warn = 3,
|
||||
Error = 4,
|
||||
Fatal = 5,
|
||||
Off = 6
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 日志服务接口
|
||||
*/
|
||||
class ILogger : public IService {
|
||||
public:
|
||||
virtual ~ILogger() = default;
|
||||
|
||||
/**
|
||||
* @brief 设置日志级别
|
||||
*/
|
||||
virtual void setLevel(LogLevel level) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取日志级别
|
||||
*/
|
||||
virtual LogLevel getLevel() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查日志级别是否启用
|
||||
*/
|
||||
virtual bool isEnabled(LogLevel level) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 记录日志(格式化)
|
||||
*/
|
||||
virtual void log(LogLevel level, const char* fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief 记录日志(字符串)
|
||||
*/
|
||||
virtual void log(LogLevel level, const std::string& msg) = 0;
|
||||
|
||||
/**
|
||||
* @brief Trace级别日志
|
||||
*/
|
||||
virtual void trace(const char* fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Debug级别日志
|
||||
*/
|
||||
virtual void debug(const char* fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Info级别日志
|
||||
*/
|
||||
virtual void info(const char* fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Warn级别日志
|
||||
*/
|
||||
virtual void warn(const char* fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Error级别日志
|
||||
*/
|
||||
virtual void error(const char* fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Fatal级别日志
|
||||
*/
|
||||
virtual void fatal(const char* fmt, ...) = 0;
|
||||
|
||||
ServiceInfo getServiceInfo() const override {
|
||||
ServiceInfo info;
|
||||
info.name = "Logger";
|
||||
info.priority = ServicePriority::Core;
|
||||
info.enabled = true;
|
||||
return info;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 控制台日志服务实现
|
||||
*/
|
||||
class ConsoleLogger : public ILogger {
|
||||
public:
|
||||
ConsoleLogger();
|
||||
~ConsoleLogger() override;
|
||||
|
||||
bool initialize() override;
|
||||
void shutdown() override;
|
||||
|
||||
void setLevel(LogLevel level) override;
|
||||
LogLevel getLevel() const override;
|
||||
bool isEnabled(LogLevel level) const override;
|
||||
|
||||
void log(LogLevel level, const char* fmt, ...) override;
|
||||
void log(LogLevel level, const std::string& msg) override;
|
||||
|
||||
void trace(const char* fmt, ...) override;
|
||||
void debug(const char* fmt, ...) override;
|
||||
void info(const char* fmt, ...) override;
|
||||
void warn(const char* fmt, ...) override;
|
||||
void error(const char* fmt, ...) override;
|
||||
void fatal(const char* fmt, ...) override;
|
||||
|
||||
private:
|
||||
void output(LogLevel level, const char* msg);
|
||||
const char* getLevelString(LogLevel level);
|
||||
|
||||
LogLevel level_;
|
||||
class Impl;
|
||||
UniquePtr<Impl> impl_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
||||
// 格式化辅助函数 - 将参数转换为字符串
|
||||
namespace extra2d {
|
||||
namespace detail {
|
||||
template<typename T>
|
||||
std::string to_string(T&& value) {
|
||||
using Decayed = std::decay_t<T>;
|
||||
if constexpr (std::is_same_v<Decayed, std::string>) {
|
||||
return value;
|
||||
} else if constexpr (std::is_same_v<Decayed, const char*>) {
|
||||
return value ? value : "(null)";
|
||||
} else if constexpr (std::is_arithmetic_v<Decayed>) {
|
||||
if constexpr (std::is_same_v<Decayed, bool>) {
|
||||
return value ? "true" : "false";
|
||||
} else if constexpr (std::is_floating_point_v<Decayed>) {
|
||||
return std::to_string(value);
|
||||
} else {
|
||||
return std::to_string(value);
|
||||
}
|
||||
} else {
|
||||
return "<?>";
|
||||
}
|
||||
}
|
||||
|
||||
inline void format_impl(std::string& result, const char* fmt) {
|
||||
result += fmt;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void format_impl(std::string& result, const char* fmt, T&& value, Args&&... args) {
|
||||
const char* p = fmt;
|
||||
while (*p) {
|
||||
if (*p == '{' && *(p + 1) == '}') {
|
||||
result += to_string(std::forward<T>(value));
|
||||
format_impl(result, p + 2, std::forward<Args>(args)...);
|
||||
return;
|
||||
}
|
||||
result += *p++;
|
||||
}
|
||||
// 没有更多的 {},追加剩余参数(不应该发生)
|
||||
result += " ";
|
||||
result += to_string(std::forward<T>(value));
|
||||
format_impl(result, p, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
std::string format_str(const char* fmt, Args&&... args) {
|
||||
if constexpr (sizeof...(args) == 0) {
|
||||
return std::string(fmt);
|
||||
} else {
|
||||
std::string result;
|
||||
detail::format_impl(result, fmt, std::forward<Args>(args)...);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 便捷宏 - 自动获取日志服务
|
||||
#define E2D_LOG(level, ...) \
|
||||
do { \
|
||||
if (auto logService = ::extra2d::ServiceLocator::instance().tryGetService<::extra2d::ILogger>()) { \
|
||||
if (logService->isEnabled(level)) { \
|
||||
logService->log(level, ::extra2d::format_str(__VA_ARGS__)); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define E2D_LOG_TRACE(...) \
|
||||
E2D_LOG(::extra2d::LogLevel::Trace, __VA_ARGS__)
|
||||
|
||||
#define E2D_LOG_DEBUG(...) \
|
||||
E2D_LOG(::extra2d::LogLevel::Debug, __VA_ARGS__)
|
||||
|
||||
#define E2D_LOG_INFO(...) \
|
||||
E2D_LOG(::extra2d::LogLevel::Info, __VA_ARGS__)
|
||||
|
||||
#define E2D_LOG_WARN(...) \
|
||||
E2D_LOG(::extra2d::LogLevel::Warn, __VA_ARGS__)
|
||||
|
||||
#define E2D_LOG_ERROR(...) \
|
||||
E2D_LOG(::extra2d::LogLevel::Error, __VA_ARGS__)
|
||||
|
||||
#define E2D_LOG_FATAL(...) \
|
||||
E2D_LOG(::extra2d::LogLevel::Fatal, __VA_ARGS__)
|
||||
|
||||
// 简写宏
|
||||
#define E2D_INFO(...) E2D_LOG_INFO(__VA_ARGS__)
|
||||
#define E2D_WARN(...) E2D_LOG_WARN(__VA_ARGS__)
|
||||
#define E2D_ERROR(...) E2D_LOG_ERROR(__VA_ARGS__)
|
||||
#define E2D_FATAL(...) E2D_LOG_FATAL(__VA_ARGS__)
|
||||
|
||||
#ifdef E2D_DEBUG
|
||||
#define E2D_DEBUG_LOG(...) E2D_LOG_DEBUG(__VA_ARGS__)
|
||||
#define E2D_TRACE(...) E2D_LOG_TRACE(__VA_ARGS__)
|
||||
#else
|
||||
#define E2D_DEBUG_LOG(...)
|
||||
#define E2D_TRACE(...)
|
||||
#endif
|
||||
|
|
@ -1,201 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
/**
|
||||
* @file logger.h
|
||||
* @brief 日志工具头文件
|
||||
*
|
||||
* 提供便捷的日志宏定义,实际实现位于 logger_service.h
|
||||
*/
|
||||
|
||||
namespace extra2d {
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
|
||||
enum class LogLevel {
|
||||
Trace = 0,
|
||||
Debug = 1,
|
||||
Info = 2,
|
||||
Warn = 3,
|
||||
Error = 4,
|
||||
Fatal = 5,
|
||||
Off = 6
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T> inline std::string to_string_arg(const T &value) {
|
||||
if constexpr (std::is_same_v<T, std::string>) {
|
||||
return value;
|
||||
} else if constexpr (std::is_same_v<T, const char *> ||
|
||||
std::is_same_v<T, char *>) {
|
||||
return value ? std::string(value) : std::string("(null)");
|
||||
} else if constexpr (std::is_same_v<T, bool>) {
|
||||
return value ? "true" : "false";
|
||||
} else if constexpr (std::is_arithmetic_v<T>) {
|
||||
if constexpr (std::is_floating_point_v<T>) {
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "%.2f", static_cast<double>(value));
|
||||
return buf;
|
||||
} else {
|
||||
return std::to_string(value);
|
||||
}
|
||||
} else {
|
||||
std::ostringstream oss;
|
||||
oss << value;
|
||||
return oss.str();
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string format_impl(const char *fmt) {
|
||||
std::string result;
|
||||
while (*fmt) {
|
||||
if (*fmt == '{' && *(fmt + 1) == '}') {
|
||||
result += "{}";
|
||||
fmt += 2;
|
||||
} else {
|
||||
result += *fmt;
|
||||
++fmt;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
inline std::string format_impl(const char *fmt, const T &first,
|
||||
const Args &...rest) {
|
||||
std::string result;
|
||||
while (*fmt) {
|
||||
if (*fmt == '{') {
|
||||
if (*(fmt + 1) == '}') {
|
||||
result += to_string_arg(first);
|
||||
fmt += 2;
|
||||
result += format_impl(fmt, rest...);
|
||||
return result;
|
||||
} else if (*(fmt + 1) == ':') {
|
||||
const char *end = fmt + 2;
|
||||
while (*end && *end != '}')
|
||||
++end;
|
||||
if (*end == '}') {
|
||||
std::string spec(fmt + 2, end);
|
||||
if (spec.find('x') != std::string::npos ||
|
||||
spec.find('X') != std::string::npos) {
|
||||
if constexpr (std::is_integral_v<T>) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "0x%x",
|
||||
static_cast<unsigned int>(first));
|
||||
result += buf;
|
||||
} else {
|
||||
result += to_string_arg(first);
|
||||
}
|
||||
} else if (spec.find('f') != std::string::npos ||
|
||||
spec.find('.') != std::string::npos) {
|
||||
if constexpr (std::is_arithmetic_v<T>) {
|
||||
int precision = 2;
|
||||
auto dot = spec.find('.');
|
||||
if (dot != std::string::npos) {
|
||||
precision = 0;
|
||||
for (size_t i = dot + 1;
|
||||
i < spec.size() && spec[i] >= '0' && spec[i] <= '9'; ++i) {
|
||||
precision = precision * 10 + (spec[i] - '0');
|
||||
}
|
||||
}
|
||||
char fmtbuf[16];
|
||||
snprintf(fmtbuf, sizeof(fmtbuf), "%%.%df", precision);
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), fmtbuf, static_cast<double>(first));
|
||||
result += buf;
|
||||
} else {
|
||||
result += to_string_arg(first);
|
||||
}
|
||||
} else {
|
||||
result += to_string_arg(first);
|
||||
}
|
||||
fmt = end + 1;
|
||||
result += format_impl(fmt, rest...);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
result += *fmt;
|
||||
++fmt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename... Args>
|
||||
inline std::string e2d_format(const char *fmt, const Args &...args) {
|
||||
return detail::format_impl(fmt, args...);
|
||||
}
|
||||
|
||||
inline std::string e2d_format(const char *fmt) { return std::string(fmt); }
|
||||
|
||||
class Logger {
|
||||
public:
|
||||
static void init();
|
||||
static void shutdown();
|
||||
|
||||
static void setLevel(LogLevel level);
|
||||
static void setConsoleOutput(bool enable);
|
||||
static void setFileOutput(const std::string &filename);
|
||||
|
||||
static LogLevel getLevel() { return level_; }
|
||||
|
||||
template <typename... Args>
|
||||
static void log(LogLevel level, const char *fmt, const Args &...args) {
|
||||
if (static_cast<int>(level) < static_cast<int>(level_))
|
||||
return;
|
||||
std::string msg = e2d_format(fmt, args...);
|
||||
outputLog(level, msg.c_str());
|
||||
}
|
||||
|
||||
static void log(LogLevel level, const char *msg) {
|
||||
if (static_cast<int>(level) < static_cast<int>(level_))
|
||||
return;
|
||||
outputLog(level, msg);
|
||||
}
|
||||
|
||||
private:
|
||||
static void outputLog(LogLevel level, const char *msg);
|
||||
static const char *getLevelString(LogLevel level);
|
||||
static void writeToConsole(LogLevel level, const char *msg);
|
||||
static void writeToFile(LogLevel level, const char *msg);
|
||||
|
||||
static LogLevel level_;
|
||||
static bool initialized_;
|
||||
static bool consoleOutput_;
|
||||
static bool fileOutput_;
|
||||
static std::string logFile_;
|
||||
static void *logFileHandle_;
|
||||
};
|
||||
|
||||
#ifdef E2D_DEBUG
|
||||
#define E2D_LOG_TRACE(...) \
|
||||
::extra2d::Logger::log(::extra2d::LogLevel::Trace, __VA_ARGS__)
|
||||
#define E2D_LOG_DEBUG(...) \
|
||||
::extra2d::Logger::log(::extra2d::LogLevel::Debug, __VA_ARGS__)
|
||||
#else
|
||||
#define E2D_LOG_TRACE(...)
|
||||
#define E2D_LOG_DEBUG(...)
|
||||
#endif
|
||||
|
||||
#define E2D_LOG_INFO(...) \
|
||||
::extra2d::Logger::log(::extra2d::LogLevel::Info, __VA_ARGS__)
|
||||
#define E2D_LOG_WARN(...) \
|
||||
::extra2d::Logger::log(::extra2d::LogLevel::Warn, __VA_ARGS__)
|
||||
#define E2D_LOG_ERROR(...) \
|
||||
::extra2d::Logger::log(::extra2d::LogLevel::Error, __VA_ARGS__)
|
||||
#define E2D_LOG_FATAL(...) \
|
||||
::extra2d::Logger::log(::extra2d::LogLevel::Fatal, __VA_ARGS__)
|
||||
|
||||
#define E2D_INFO(...) E2D_LOG_INFO(__VA_ARGS__)
|
||||
#define E2D_WARN(...) E2D_LOG_WARN(__VA_ARGS__)
|
||||
#define E2D_ERROR(...) E2D_LOG_ERROR(__VA_ARGS__)
|
||||
#define E2D_FATAL(...) E2D_LOG_FATAL(__VA_ARGS__)
|
||||
#ifdef E2D_DEBUG
|
||||
#define E2D_DEBUG_LOG(...) E2D_LOG_DEBUG(__VA_ARGS__)
|
||||
#define E2D_TRACE(...) E2D_LOG_TRACE(__VA_ARGS__)
|
||||
#else
|
||||
#define E2D_DEBUG_LOG(...)
|
||||
#define E2D_TRACE(...)
|
||||
#endif
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/config/module_config.h>
|
||||
#include <extra2d/config/module_initializer.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class LoggerModuleConfig : public IModuleConfig {
|
||||
public:
|
||||
LogLevel logLevel = LogLevel::Info;
|
||||
bool consoleOutput = true;
|
||||
bool fileOutput = false;
|
||||
std::string logFilePath;
|
||||
|
||||
ModuleInfo getModuleInfo() const override {
|
||||
ModuleInfo info;
|
||||
info.id = 0;
|
||||
info.name = "Logger";
|
||||
info.version = "1.0.0";
|
||||
info.priority = ModulePriority::Core;
|
||||
info.enabled = true;
|
||||
return info;
|
||||
}
|
||||
|
||||
std::string getConfigSectionName() const override {
|
||||
return "logger";
|
||||
}
|
||||
|
||||
bool validate() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void resetToDefaults() override {
|
||||
logLevel = LogLevel::Info;
|
||||
consoleOutput = true;
|
||||
fileOutput = false;
|
||||
logFilePath.clear();
|
||||
}
|
||||
|
||||
bool loadFromJson(const void* jsonData) override;
|
||||
bool saveToJson(void* jsonData) const override;
|
||||
};
|
||||
|
||||
class LoggerModuleInitializer : public IModuleInitializer {
|
||||
public:
|
||||
LoggerModuleInitializer();
|
||||
~LoggerModuleInitializer() override;
|
||||
|
||||
ModuleId getModuleId() const override { return moduleId_; }
|
||||
ModulePriority getPriority() const override { return ModulePriority::Core; }
|
||||
std::vector<ModuleId> getDependencies() const override { return {}; }
|
||||
|
||||
bool initialize(const IModuleConfig* config) override;
|
||||
void shutdown() override;
|
||||
bool isInitialized() const override { return initialized_; }
|
||||
|
||||
void setModuleId(ModuleId id) { moduleId_ = id; }
|
||||
|
||||
private:
|
||||
ModuleId moduleId_ = INVALID_MODULE_ID;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,18 +1,17 @@
|
|||
#include <extra2d/app/application.h>
|
||||
#include <extra2d/config/config_module.h>
|
||||
#include <extra2d/config/module_registry.h>
|
||||
#include <extra2d/graphics/render_config.h>
|
||||
#include <extra2d/graphics/render_module.h>
|
||||
#include <extra2d/graphics/vram_manager.h>
|
||||
#include <extra2d/platform/iinput.h>
|
||||
#include <extra2d/platform/input_module.h>
|
||||
#include <extra2d/platform/platform_init_module.h>
|
||||
#include <extra2d/core/registry.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/services/camera_service.h>
|
||||
#include <extra2d/services/event_service.h>
|
||||
#include <extra2d/platform/input_module.h>
|
||||
#include <extra2d/graphics/core/render_module.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <extra2d/platform/iinput.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/services/scene_service.h>
|
||||
#include <extra2d/services/timer_service.h>
|
||||
|
||||
#include <extra2d/services/event_service.h>
|
||||
#include <extra2d/services/camera_service.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <extra2d/graphics/memory/vram_manager.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
|
@ -21,391 +20,249 @@ namespace extra2d {
|
|||
|
||||
static double getTimeSeconds() {
|
||||
#ifdef __SWITCH__
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return static_cast<double>(ts.tv_sec) +
|
||||
static_cast<double>(ts.tv_nsec) / 1000000000.0;
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return static_cast<double>(ts.tv_sec) +
|
||||
static_cast<double>(ts.tv_nsec) / 1000000000.0;
|
||||
#else
|
||||
using namespace std::chrono;
|
||||
auto now = steady_clock::now();
|
||||
auto duration = now.time_since_epoch();
|
||||
return duration_cast<std::chrono::duration<double>>(duration).count();
|
||||
using namespace std::chrono;
|
||||
auto now = steady_clock::now();
|
||||
auto duration = now.time_since_epoch();
|
||||
return duration_cast<std::chrono::duration<double>>(duration).count();
|
||||
#endif
|
||||
}
|
||||
|
||||
Application &Application::get() {
|
||||
static Application instance;
|
||||
return instance;
|
||||
Application& Application::get() {
|
||||
static Application instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool Application::init() {
|
||||
AppConfig cfg;
|
||||
return init(cfg);
|
||||
}
|
||||
|
||||
bool Application::init(const AppConfig &config) {
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
register_config_module();
|
||||
register_platform_module();
|
||||
register_window_module();
|
||||
register_input_module();
|
||||
register_render_module();
|
||||
|
||||
auto *configInit =
|
||||
ModuleRegistry::instance().getInitializer(get_config_module_id());
|
||||
if (configInit) {
|
||||
auto *cfgInit = dynamic_cast<ConfigModuleInitializer *>(configInit);
|
||||
if (cfgInit) {
|
||||
cfgInit->setAppConfig(config);
|
||||
}
|
||||
}
|
||||
|
||||
return initModules();
|
||||
}
|
||||
|
||||
bool Application::init(const std::string &configPath) {
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
register_config_module();
|
||||
register_platform_module();
|
||||
register_window_module();
|
||||
register_input_module();
|
||||
register_render_module();
|
||||
|
||||
auto *configInit =
|
||||
ModuleRegistry::instance().getInitializer(get_config_module_id());
|
||||
if (configInit) {
|
||||
auto *cfgInit = dynamic_cast<ConfigModuleInitializer *>(configInit);
|
||||
if (cfgInit) {
|
||||
cfgInit->setConfigPath(configPath);
|
||||
}
|
||||
}
|
||||
|
||||
return initModules();
|
||||
}
|
||||
|
||||
void Application::registerCoreServices() {
|
||||
auto &locator = ServiceLocator::instance();
|
||||
|
||||
if (!locator.hasService<ISceneService>()) {
|
||||
locator.registerService<ISceneService>(makeShared<SceneService>());
|
||||
}
|
||||
|
||||
if (!locator.hasService<ITimerService>()) {
|
||||
locator.registerService<ITimerService>(makeShared<TimerService>());
|
||||
}
|
||||
|
||||
if (!locator.hasService<ICameraService>()) {
|
||||
auto cameraService = makeShared<CameraService>();
|
||||
if (window_) {
|
||||
cameraService->setViewport(0, static_cast<float>(window_->width()),
|
||||
static_cast<float>(window_->height()), 0);
|
||||
ViewportConfig vpConfig;
|
||||
vpConfig.logicWidth = static_cast<float>(window_->width());
|
||||
vpConfig.logicHeight = static_cast<float>(window_->height());
|
||||
vpConfig.mode = ViewportMode::AspectRatio;
|
||||
cameraService->setViewportConfig(vpConfig);
|
||||
cameraService->updateViewport(window_->width(), window_->height());
|
||||
}
|
||||
locator.registerService<ICameraService>(cameraService);
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::initModules() {
|
||||
auto &locator = ServiceLocator::instance();
|
||||
|
||||
if (!locator.hasService<IEventService>()) {
|
||||
locator.registerService<IEventService>(makeShared<EventService>());
|
||||
}
|
||||
|
||||
auto initOrder = ModuleRegistry::instance().getInitializationOrder();
|
||||
|
||||
for (ModuleId moduleId : initOrder) {
|
||||
auto *initializer = ModuleRegistry::instance().getInitializer(moduleId);
|
||||
if (!initializer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto *moduleConfig = ModuleRegistry::instance().getModuleConfig(moduleId);
|
||||
if (!moduleConfig) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto info = moduleConfig->getModuleInfo();
|
||||
if (!info.enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (info.name == "Render") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!initializer->initialize(moduleConfig)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto *windowInit =
|
||||
ModuleRegistry::instance().getInitializer(get_window_module_id());
|
||||
if (!windowInit || !windowInit->isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *windowModule = dynamic_cast<WindowModuleInitializer *>(windowInit);
|
||||
if (!windowModule) {
|
||||
return false;
|
||||
}
|
||||
|
||||
window_ = windowModule->getWindow();
|
||||
if (!window_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *renderInit =
|
||||
ModuleRegistry::instance().getInitializer(get_render_module_id());
|
||||
if (renderInit) {
|
||||
auto *renderModule = dynamic_cast<RenderModuleInitializer *>(renderInit);
|
||||
if (renderModule) {
|
||||
renderModule->setWindow(window_);
|
||||
|
||||
auto *renderConfig =
|
||||
ModuleRegistry::instance().getModuleConfig(get_render_module_id());
|
||||
if (renderConfig && !renderInit->initialize(renderConfig)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerCoreServices();
|
||||
|
||||
if (!ServiceLocator::instance().initializeAll()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto cameraService = ServiceLocator::instance().getService<ICameraService>();
|
||||
if (cameraService && window_) {
|
||||
window_->onResize([cameraService](int width, int height) {
|
||||
cameraService->updateViewport(width, height);
|
||||
cameraService->applyViewportAdapter();
|
||||
|
||||
auto sceneService =
|
||||
ServiceLocator::instance().getService<ISceneService>();
|
||||
if (sceneService) {
|
||||
auto currentScene = sceneService->getCurrentScene();
|
||||
if (currentScene) {
|
||||
currentScene->setViewportSize(static_cast<float>(width),
|
||||
static_cast<float>(height));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
running_ = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::shutdown() {
|
||||
if (!initialized_)
|
||||
return;
|
||||
|
||||
VRAMMgr::get().printStats();
|
||||
|
||||
ServiceLocator::instance().clear();
|
||||
|
||||
window_ = nullptr;
|
||||
|
||||
auto initOrder = ModuleRegistry::instance().getInitializationOrder();
|
||||
|
||||
for (auto it = initOrder.rbegin(); it != initOrder.rend(); ++it) {
|
||||
ModuleId moduleId = *it;
|
||||
auto *initializer = ModuleRegistry::instance().getInitializer(moduleId);
|
||||
if (initializer && initializer->isInitialized()) {
|
||||
initializer->shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
initialized_ = false;
|
||||
running_ = false;
|
||||
Application::Application() {
|
||||
Registry::instance().setApp(this);
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::init() {
|
||||
AppConfig cfg;
|
||||
return init(cfg);
|
||||
}
|
||||
|
||||
bool Application::init(const AppConfig& config) {
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
appConfig_ = config;
|
||||
|
||||
// 首先注册日志服务(模块初始化可能需要它)
|
||||
auto& locator = ServiceLocator::instance();
|
||||
if (!locator.hasService<ILogger>()) {
|
||||
auto logger = makeShared<ConsoleLogger>();
|
||||
locator.registerService(std::static_pointer_cast<ILogger>(logger));
|
||||
}
|
||||
|
||||
// 初始化所有模块(拓扑排序)
|
||||
if (!Registry::instance().init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 模块初始化完成后,注册其他核心服务
|
||||
registerCoreServices();
|
||||
|
||||
initialized_ = true;
|
||||
running_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::registerCoreServices() {
|
||||
auto& locator = ServiceLocator::instance();
|
||||
|
||||
if (!locator.hasService<ISceneService>()) {
|
||||
auto service = makeShared<SceneService>();
|
||||
locator.registerService(std::static_pointer_cast<ISceneService>(service));
|
||||
}
|
||||
|
||||
if (!locator.hasService<ITimerService>()) {
|
||||
auto service = makeShared<TimerService>();
|
||||
locator.registerService(std::static_pointer_cast<ITimerService>(service));
|
||||
}
|
||||
|
||||
if (!locator.hasService<IEventService>()) {
|
||||
auto service = makeShared<EventService>();
|
||||
locator.registerService(std::static_pointer_cast<IEventService>(service));
|
||||
}
|
||||
|
||||
auto* winMod = get<WindowModule>();
|
||||
if (winMod && winMod->win() && !locator.hasService<ICameraService>()) {
|
||||
auto cameraService = makeShared<CameraService>();
|
||||
auto* win = winMod->win();
|
||||
cameraService->setViewport(0, static_cast<float>(win->width()),
|
||||
static_cast<float>(win->height()), 0);
|
||||
ViewportConfig vpConfig;
|
||||
vpConfig.logicWidth = static_cast<float>(win->width());
|
||||
vpConfig.logicHeight = static_cast<float>(win->height());
|
||||
vpConfig.mode = ViewportMode::AspectRatio;
|
||||
cameraService->setViewportConfig(vpConfig);
|
||||
cameraService->updateViewport(win->width(), win->height());
|
||||
locator.registerService(std::static_pointer_cast<ICameraService>(cameraService));
|
||||
|
||||
win->onResize([cameraService](int width, int height) {
|
||||
cameraService->updateViewport(width, height);
|
||||
cameraService->applyViewportAdapter();
|
||||
});
|
||||
}
|
||||
|
||||
locator.initializeAll();
|
||||
}
|
||||
|
||||
void Application::shutdown() {
|
||||
if (!initialized_) return;
|
||||
|
||||
VRAMMgr::get().printStats();
|
||||
|
||||
ServiceLocator::instance().clear();
|
||||
Registry::instance().shutdown();
|
||||
Registry::instance().clear();
|
||||
|
||||
initialized_ = false;
|
||||
running_ = false;
|
||||
}
|
||||
|
||||
void Application::run() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastFrameTime_ = getTimeSeconds();
|
||||
|
||||
while (running_ && !window_->shouldClose()) {
|
||||
mainLoop();
|
||||
}
|
||||
if (!initialized_) return;
|
||||
|
||||
auto* winMod = get<WindowModule>();
|
||||
if (!winMod || !winMod->win()) return;
|
||||
|
||||
lastFrameTime_ = getTimeSeconds();
|
||||
|
||||
while (running_ && !winMod->win()->shouldClose()) {
|
||||
mainLoop();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::quit() {
|
||||
shouldQuit_ = true;
|
||||
running_ = false;
|
||||
shouldQuit_ = true;
|
||||
running_ = false;
|
||||
}
|
||||
|
||||
void Application::pause() {
|
||||
if (!paused_) {
|
||||
paused_ = true;
|
||||
ServiceLocator::instance().pauseAll();
|
||||
}
|
||||
if (!paused_) {
|
||||
paused_ = true;
|
||||
ServiceLocator::instance().pauseAll();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::resume() {
|
||||
if (paused_) {
|
||||
paused_ = false;
|
||||
ServiceLocator::instance().resumeAll();
|
||||
lastFrameTime_ = getTimeSeconds();
|
||||
}
|
||||
if (paused_) {
|
||||
paused_ = false;
|
||||
ServiceLocator::instance().resumeAll();
|
||||
lastFrameTime_ = getTimeSeconds();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::mainLoop() {
|
||||
double currentTime = getTimeSeconds();
|
||||
deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
|
||||
lastFrameTime_ = currentTime;
|
||||
|
||||
totalTime_ += deltaTime_;
|
||||
|
||||
frameCount_++;
|
||||
fpsTimer_ += deltaTime_;
|
||||
if (fpsTimer_ >= 1.0f) {
|
||||
currentFps_ = frameCount_;
|
||||
frameCount_ = 0;
|
||||
fpsTimer_ -= 1.0f;
|
||||
}
|
||||
|
||||
window_->poll();
|
||||
|
||||
auto eventService = ServiceLocator::instance().getService<IEventService>();
|
||||
if (eventService) {
|
||||
eventService->processQueue();
|
||||
}
|
||||
|
||||
if (!paused_) {
|
||||
update();
|
||||
}
|
||||
|
||||
render();
|
||||
|
||||
const auto &appConfig = ConfigManager::instance().appConfig();
|
||||
|
||||
auto *renderConfig =
|
||||
ModuleRegistry::instance().getModuleConfig(get_render_module_id());
|
||||
auto *renderModuleConfig =
|
||||
dynamic_cast<const RenderModuleConfig *>(renderConfig);
|
||||
|
||||
if (renderModuleConfig && !renderModuleConfig->vsync &&
|
||||
renderModuleConfig->targetFPS > 0) {
|
||||
double frameEndTime = getTimeSeconds();
|
||||
double frameTime = frameEndTime - currentTime;
|
||||
double target = 1.0 / static_cast<double>(renderModuleConfig->targetFPS);
|
||||
if (frameTime < target) {
|
||||
auto sleepSeconds = target - frameTime;
|
||||
std::this_thread::sleep_for(std::chrono::duration<double>(sleepSeconds));
|
||||
double currentTime = getTimeSeconds();
|
||||
deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
|
||||
lastFrameTime_ = currentTime;
|
||||
|
||||
totalTime_ += deltaTime_;
|
||||
|
||||
frameCount_++;
|
||||
fpsTimer_ += deltaTime_;
|
||||
if (fpsTimer_ >= 1.0f) {
|
||||
currentFps_ = frameCount_;
|
||||
frameCount_ = 0;
|
||||
fpsTimer_ -= 1.0f;
|
||||
}
|
||||
|
||||
auto* winMod = get<WindowModule>();
|
||||
if (winMod && winMod->win()) {
|
||||
winMod->win()->poll();
|
||||
}
|
||||
|
||||
auto eventService = ServiceLocator::instance().getService<IEventService>();
|
||||
if (eventService) {
|
||||
eventService->processQueue();
|
||||
}
|
||||
|
||||
if (!paused_) {
|
||||
update();
|
||||
}
|
||||
|
||||
render();
|
||||
|
||||
// 帧率限制
|
||||
auto* renderMod = get<RenderModule>();
|
||||
if (renderMod && renderMod->renderer()) {
|
||||
// 这里可以添加帧率限制逻辑
|
||||
}
|
||||
}
|
||||
|
||||
ConfigManager::instance().update(deltaTime_);
|
||||
}
|
||||
|
||||
void Application::update() { ServiceLocator::instance().updateAll(deltaTime_); }
|
||||
void Application::update() {
|
||||
ServiceLocator::instance().updateAll(deltaTime_);
|
||||
|
||||
auto* inputMod = get<InputModule>();
|
||||
if (inputMod) {
|
||||
inputMod->update();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::render() {
|
||||
auto *renderInit =
|
||||
ModuleRegistry::instance().getInitializer(get_render_module_id());
|
||||
RenderBackend *renderer = nullptr;
|
||||
if (renderInit) {
|
||||
auto *renderModule = dynamic_cast<RenderModuleInitializer *>(renderInit);
|
||||
if (renderModule) {
|
||||
renderer = renderModule->getRenderer();
|
||||
auto* renderMod = get<RenderModule>();
|
||||
if (!renderMod || !renderMod->renderer()) return;
|
||||
|
||||
auto* renderer = renderMod->renderer();
|
||||
auto* winMod = get<WindowModule>();
|
||||
if (!winMod || !winMod->win()) return;
|
||||
|
||||
auto cameraService = ServiceLocator::instance().getService<ICameraService>();
|
||||
if (cameraService) {
|
||||
const auto& vp = cameraService->getViewportResult().viewport;
|
||||
renderer->setViewport(
|
||||
static_cast<int>(vp.origin.x), static_cast<int>(vp.origin.y),
|
||||
static_cast<int>(vp.size.width), static_cast<int>(vp.size.height));
|
||||
renderer->setViewProjection(cameraService->getViewProjectionMatrix());
|
||||
} else {
|
||||
renderer->setViewport(0, 0, winMod->win()->width(), winMod->win()->height());
|
||||
}
|
||||
}
|
||||
|
||||
if (!renderer) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto cameraService = ServiceLocator::instance().getService<ICameraService>();
|
||||
if (cameraService) {
|
||||
const auto &vp = cameraService->getViewportResult().viewport;
|
||||
renderer->setViewport(
|
||||
static_cast<int>(vp.origin.x), static_cast<int>(vp.origin.y),
|
||||
static_cast<int>(vp.size.width), static_cast<int>(vp.size.height));
|
||||
|
||||
renderer->setViewProjection(cameraService->getViewProjectionMatrix());
|
||||
} else {
|
||||
renderer->setViewport(0, 0, window_->width(), window_->height());
|
||||
}
|
||||
|
||||
auto sceneService = ServiceLocator::instance().getService<ISceneService>();
|
||||
if (sceneService) {
|
||||
sceneService->render(*renderer);
|
||||
}
|
||||
|
||||
window_->swap();
|
||||
}
|
||||
|
||||
IInput &Application::input() { return *window_->input(); }
|
||||
|
||||
RenderBackend &Application::renderer() {
|
||||
auto *renderInit =
|
||||
ModuleRegistry::instance().getInitializer(get_render_module_id());
|
||||
if (renderInit) {
|
||||
auto *renderModule = dynamic_cast<RenderModuleInitializer *>(renderInit);
|
||||
if (renderModule && renderModule->getRenderer()) {
|
||||
return *renderModule->getRenderer();
|
||||
|
||||
auto sceneService = ServiceLocator::instance().getService<ISceneService>();
|
||||
if (sceneService) {
|
||||
sceneService->render(*renderer);
|
||||
}
|
||||
}
|
||||
static RenderBackend *dummy = nullptr;
|
||||
if (!dummy) {
|
||||
dummy = RenderBackend::create(BackendType::OpenGL).release();
|
||||
}
|
||||
return *dummy;
|
||||
|
||||
winMod->win()->swap();
|
||||
}
|
||||
|
||||
SharedPtr<ISceneService> Application::scenes() {
|
||||
return ServiceLocator::instance().getService<ISceneService>();
|
||||
IWindow* Application::window() {
|
||||
auto* winMod = get<WindowModule>();
|
||||
return winMod ? winMod->win() : nullptr;
|
||||
}
|
||||
|
||||
SharedPtr<ITimerService> Application::timers() {
|
||||
return ServiceLocator::instance().getService<ITimerService>();
|
||||
RenderBackend* Application::renderer() {
|
||||
auto* renderMod = get<RenderModule>();
|
||||
return renderMod ? renderMod->renderer() : nullptr;
|
||||
}
|
||||
|
||||
SharedPtr<IEventService> Application::events() {
|
||||
return ServiceLocator::instance().getService<IEventService>();
|
||||
}
|
||||
|
||||
SharedPtr<ICameraService> Application::camera() {
|
||||
return ServiceLocator::instance().getService<ICameraService>();
|
||||
IInput* Application::input() {
|
||||
auto* winMod = get<WindowModule>();
|
||||
return (winMod && winMod->win()) ? winMod->win()->input() : nullptr;
|
||||
}
|
||||
|
||||
void Application::enterScene(Ptr<Scene> scene) {
|
||||
auto sceneService = ServiceLocator::instance().getService<ISceneService>();
|
||||
if (sceneService && scene) {
|
||||
scene->setViewportSize(static_cast<float>(window_->width()),
|
||||
static_cast<float>(window_->height()));
|
||||
sceneService->enterScene(scene);
|
||||
}
|
||||
}
|
||||
|
||||
ConfigManager &Application::config() { return ConfigManager::instance(); }
|
||||
|
||||
const AppConfig &Application::getConfig() const {
|
||||
return ConfigManager::instance().appConfig();
|
||||
auto sceneService = ServiceLocator::instance().getService<ISceneService>();
|
||||
auto* winMod = get<WindowModule>();
|
||||
if (sceneService && scene && winMod && winMod->win()) {
|
||||
scene->setViewportSize(static_cast<float>(winMod->win()->width()),
|
||||
static_cast<float>(winMod->win()->height()));
|
||||
sceneService->enterScene(scene);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,341 +0,0 @@
|
|||
#include <extra2d/config/config_loader.h>
|
||||
#include <extra2d/config/config_manager.h>
|
||||
#include <extra2d/config/module_registry.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 去除字符串首尾空白字符
|
||||
* @param str 输入字符串
|
||||
* @return 去除空白后的字符串
|
||||
*/
|
||||
static std::string trim(const std::string& str) {
|
||||
size_t start = 0;
|
||||
while (start < str.length() && std::isspace(static_cast<unsigned char>(str[start]))) {
|
||||
++start;
|
||||
}
|
||||
size_t end = str.length();
|
||||
while (end > start && std::isspace(static_cast<unsigned char>(str[end - 1]))) {
|
||||
--end;
|
||||
}
|
||||
return str.substr(start, end - start);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将字符串转换为小写
|
||||
* @param str 输入字符串
|
||||
* @return 小写字符串
|
||||
*/
|
||||
static std::string toLower(const std::string& str) {
|
||||
std::string result = str;
|
||||
for (char& c : result) {
|
||||
c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief INI 数据存储结构
|
||||
*/
|
||||
using IniData = std::map<std::string, std::map<std::string, std::string>>;
|
||||
|
||||
/**
|
||||
* @brief 解析 INI 内容到数据结构
|
||||
* @param content INI 内容字符串
|
||||
* @param data 输出的 INI 数据
|
||||
* @return 解析结果
|
||||
*/
|
||||
static ConfigLoadResult parseIniContent(const std::string& content, IniData& data) {
|
||||
std::istringstream stream(content);
|
||||
std::string line;
|
||||
std::string currentSection;
|
||||
int lineNumber = 0;
|
||||
|
||||
while (std::getline(stream, line)) {
|
||||
++lineNumber;
|
||||
line = trim(line);
|
||||
|
||||
if (line.empty() || line[0] == ';' || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line[0] == '[') {
|
||||
size_t endBracket = line.find(']');
|
||||
if (endBracket == std::string::npos) {
|
||||
return ConfigLoadResult::error("INI 解析错误: 缺少右括号", lineNumber);
|
||||
}
|
||||
currentSection = trim(line.substr(1, endBracket - 1));
|
||||
if (data.find(currentSection) == data.end()) {
|
||||
data[currentSection] = std::map<std::string, std::string>();
|
||||
}
|
||||
} else {
|
||||
size_t equalPos = line.find('=');
|
||||
if (equalPos == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string key = trim(line.substr(0, equalPos));
|
||||
std::string value = trim(line.substr(equalPos + 1));
|
||||
|
||||
if (currentSection.empty()) {
|
||||
return ConfigLoadResult::error("INI 解析错误: 键值对不在任何节中", lineNumber);
|
||||
}
|
||||
|
||||
data[currentSection][key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return ConfigLoadResult::ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取 INI 值(带默认值)
|
||||
* @param data INI 数据
|
||||
* @param section 节名
|
||||
* @param key 键名
|
||||
* @param defaultValue 默认值
|
||||
* @return 值字符串
|
||||
*/
|
||||
static std::string getIniValue(const IniData& data, const std::string& section,
|
||||
const std::string& key, const std::string& defaultValue = "") {
|
||||
auto sectionIt = data.find(section);
|
||||
if (sectionIt == data.end()) {
|
||||
return defaultValue;
|
||||
}
|
||||
auto keyIt = sectionIt->second.find(key);
|
||||
if (keyIt == sectionIt->second.end()) {
|
||||
return defaultValue;
|
||||
}
|
||||
return keyIt->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查 INI 值是否存在
|
||||
* @param data INI 数据
|
||||
* @param section 节名
|
||||
* @param key 键名
|
||||
* @return 是否存在
|
||||
*/
|
||||
static bool hasIniValue(const IniData& data, const std::string& section, const std::string& key) {
|
||||
auto sectionIt = data.find(section);
|
||||
if (sectionIt == data.end()) {
|
||||
return false;
|
||||
}
|
||||
return sectionIt->second.find(key) != sectionIt->second.end();
|
||||
}
|
||||
|
||||
ConfigLoadResult IniConfigLoader::load(const std::string& filepath, AppConfig& config) {
|
||||
E2D_LOG_INFO("正在从 INI 文件加载应用配置: {}", filepath);
|
||||
|
||||
std::ifstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("无法打开配置文件: {}", filepath);
|
||||
return ConfigLoadResult::error("无法打开配置文件: " + filepath);
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
std::string content = buffer.str();
|
||||
file.close();
|
||||
|
||||
return loadFromString(content, config);
|
||||
}
|
||||
|
||||
ConfigSaveResult IniConfigLoader::save(const std::string& filepath, const AppConfig& config) {
|
||||
E2D_LOG_INFO("正在保存应用配置到 INI 文件: {}", filepath);
|
||||
|
||||
std::string content = saveToString(config);
|
||||
|
||||
std::ofstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("无法创建配置文件: {}", filepath);
|
||||
return ConfigSaveResult::error("无法创建配置文件: " + filepath);
|
||||
}
|
||||
|
||||
file << content;
|
||||
file.close();
|
||||
|
||||
E2D_LOG_INFO("配置已成功保存到: {}", filepath);
|
||||
return ConfigSaveResult::ok();
|
||||
}
|
||||
|
||||
ConfigLoadResult IniConfigLoader::loadFromString(const std::string& content, AppConfig& config) {
|
||||
IniData data;
|
||||
auto result = parseIniContent(content, data);
|
||||
if (result.hasError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (hasIniValue(data, "app", "name")) {
|
||||
config.appName = getIniValue(data, "app", "name");
|
||||
}
|
||||
if (hasIniValue(data, "app", "version")) {
|
||||
config.appVersion = getIniValue(data, "app", "version");
|
||||
}
|
||||
if (hasIniValue(data, "app", "organization")) {
|
||||
config.organization = getIniValue(data, "app", "organization");
|
||||
}
|
||||
if (hasIniValue(data, "app", "configFile")) {
|
||||
config.configFile = getIniValue(data, "app", "configFile");
|
||||
}
|
||||
if (hasIniValue(data, "app", "targetPlatform")) {
|
||||
int value;
|
||||
auto res = parseInt(getIniValue(data, "app", "targetPlatform"), value, "app.targetPlatform");
|
||||
if (res.isOk()) {
|
||||
config.targetPlatform = static_cast<PlatformType>(value);
|
||||
}
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("INI 应用配置加载成功");
|
||||
return ConfigLoadResult::ok();
|
||||
}
|
||||
|
||||
std::string IniConfigLoader::saveToString(const AppConfig& config) {
|
||||
std::ostringstream oss;
|
||||
|
||||
oss << "[app]\n";
|
||||
oss << "name=" << config.appName << "\n";
|
||||
oss << "version=" << config.appVersion << "\n";
|
||||
oss << "organization=" << config.organization << "\n";
|
||||
oss << "configFile=" << config.configFile << "\n";
|
||||
oss << "targetPlatform=" << static_cast<int>(config.targetPlatform) << "\n";
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
ConfigLoadResult IniConfigLoader::loadWithModules(const std::string& filepath) {
|
||||
E2D_LOG_INFO("正在从 INI 文件加载完整配置(含模块): {}", filepath);
|
||||
|
||||
std::ifstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("无法打开配置文件: {}", filepath);
|
||||
return ConfigLoadResult::error("无法打开配置文件: " + filepath);
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
std::string content = buffer.str();
|
||||
file.close();
|
||||
|
||||
IniData data;
|
||||
auto result = parseIniContent(content, data);
|
||||
if (result.hasError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto& registry = ModuleRegistry::instance();
|
||||
auto moduleIds = registry.getAllModules();
|
||||
|
||||
for (ModuleId moduleId : moduleIds) {
|
||||
IModuleConfig* moduleConfig = registry.getModuleConfig(moduleId);
|
||||
if (!moduleConfig) continue;
|
||||
|
||||
std::string sectionName = moduleConfig->getConfigSectionName();
|
||||
if (sectionName.empty()) continue;
|
||||
|
||||
if (data.find(sectionName) != data.end()) {
|
||||
E2D_LOG_DEBUG("加载模块 {} 的 INI 配置", moduleConfig->getModuleInfo().name);
|
||||
}
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("完整配置加载成功");
|
||||
return ConfigLoadResult::ok();
|
||||
}
|
||||
|
||||
ConfigSaveResult IniConfigLoader::saveWithModules(const std::string& filepath) {
|
||||
E2D_LOG_INFO("正在保存完整配置(含模块)到 INI 文件: {}", filepath);
|
||||
|
||||
std::ostringstream oss;
|
||||
|
||||
oss << saveToString(ConfigManager::instance().appConfig());
|
||||
|
||||
auto& registry = ModuleRegistry::instance();
|
||||
auto moduleIds = registry.getAllModules();
|
||||
|
||||
for (ModuleId moduleId : moduleIds) {
|
||||
IModuleConfig* moduleConfig = registry.getModuleConfig(moduleId);
|
||||
if (!moduleConfig) continue;
|
||||
|
||||
std::string sectionName = moduleConfig->getConfigSectionName();
|
||||
if (sectionName.empty()) continue;
|
||||
|
||||
oss << "\n[" << sectionName << "]\n";
|
||||
}
|
||||
|
||||
std::ofstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("无法创建配置文件: {}", filepath);
|
||||
return ConfigSaveResult::error("无法创建配置文件: " + filepath);
|
||||
}
|
||||
|
||||
file << oss.str();
|
||||
file.close();
|
||||
|
||||
E2D_LOG_INFO("完整配置已成功保存到: {}", filepath);
|
||||
return ConfigSaveResult::ok();
|
||||
}
|
||||
|
||||
bool IniConfigLoader::supportsFile(const std::string& filepath) const {
|
||||
if (filepath.length() >= 4) {
|
||||
std::string ext = filepath.substr(filepath.length() - 4);
|
||||
for (char& c : ext) c = static_cast<char>(std::tolower(c));
|
||||
return ext == ".ini";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
UniquePtr<ConfigLoader> IniConfigLoader::clone() const {
|
||||
return makeUnique<IniConfigLoader>();
|
||||
}
|
||||
|
||||
std::string IniConfigLoader::sectionKey(const std::string& section, const std::string& key) const {
|
||||
return section + "." + key;
|
||||
}
|
||||
|
||||
ConfigLoadResult IniConfigLoader::parseInt(const std::string& value, int& result, const std::string& fieldName) {
|
||||
try {
|
||||
size_t pos;
|
||||
result = std::stoi(value, &pos);
|
||||
if (pos != value.length()) {
|
||||
return ConfigLoadResult::error("无法解析整数值: " + value, -1, fieldName);
|
||||
}
|
||||
return ConfigLoadResult::ok();
|
||||
} catch (const std::exception& e) {
|
||||
return ConfigLoadResult::error(std::string("解析整数失败: ") + e.what(), -1, fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
ConfigLoadResult IniConfigLoader::parseFloat(const std::string& value, float& result, const std::string& fieldName) {
|
||||
try {
|
||||
size_t pos;
|
||||
result = std::stof(value, &pos);
|
||||
if (pos != value.length()) {
|
||||
return ConfigLoadResult::error("无法解析浮点数值: " + value, -1, fieldName);
|
||||
}
|
||||
return ConfigLoadResult::ok();
|
||||
} catch (const std::exception& e) {
|
||||
return ConfigLoadResult::error(std::string("解析浮点数失败: ") + e.what(), -1, fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
ConfigLoadResult IniConfigLoader::parseBool(const std::string& value, bool& result, const std::string& fieldName) {
|
||||
std::string lower = toLower(value);
|
||||
if (lower == "true" || lower == "1" || lower == "yes" || lower == "on") {
|
||||
result = true;
|
||||
return ConfigLoadResult::ok();
|
||||
}
|
||||
if (lower == "false" || lower == "0" || lower == "no" || lower == "off") {
|
||||
result = false;
|
||||
return ConfigLoadResult::ok();
|
||||
}
|
||||
return ConfigLoadResult::error("无法解析布尔值: " + value, -1, fieldName);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,188 +0,0 @@
|
|||
#include <extra2d/config/config_loader.h>
|
||||
#include <extra2d/config/module_registry.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
ConfigLoadResult JsonConfigLoader::load(const std::string& filepath, AppConfig& config) {
|
||||
E2D_LOG_INFO("正在从 JSON 文件加载应用配置: {}", filepath);
|
||||
|
||||
std::ifstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("无法打开配置文件: {}", filepath);
|
||||
return ConfigLoadResult::error("无法打开配置文件: " + filepath);
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
std::string content = buffer.str();
|
||||
file.close();
|
||||
|
||||
return loadFromString(content, config);
|
||||
}
|
||||
|
||||
ConfigSaveResult JsonConfigLoader::save(const std::string& filepath, const AppConfig& config) {
|
||||
E2D_LOG_INFO("正在保存应用配置到 JSON 文件: {}", filepath);
|
||||
|
||||
std::string content = saveToString(config);
|
||||
|
||||
std::ofstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("无法创建配置文件: {}", filepath);
|
||||
return ConfigSaveResult::error("无法创建配置文件: " + filepath);
|
||||
}
|
||||
|
||||
file << content;
|
||||
file.close();
|
||||
|
||||
E2D_LOG_INFO("配置已成功保存到: {}", filepath);
|
||||
return ConfigSaveResult::ok();
|
||||
}
|
||||
|
||||
ConfigLoadResult JsonConfigLoader::loadFromString(const std::string& content, AppConfig& config) {
|
||||
json root;
|
||||
|
||||
try {
|
||||
root = json::parse(content);
|
||||
} catch (const json::parse_error& e) {
|
||||
E2D_LOG_ERROR("JSON 解析错误: {}", e.what());
|
||||
return ConfigLoadResult::error(std::string("JSON 解析错误: ") + e.what(),
|
||||
static_cast<int>(e.byte));
|
||||
}
|
||||
|
||||
if (root.contains("appName") && root["appName"].is_string()) {
|
||||
config.appName = root["appName"].get<std::string>();
|
||||
}
|
||||
|
||||
if (root.contains("appVersion") && root["appVersion"].is_string()) {
|
||||
config.appVersion = root["appVersion"].get<std::string>();
|
||||
}
|
||||
|
||||
if (root.contains("organization") && root["organization"].is_string()) {
|
||||
config.organization = root["organization"].get<std::string>();
|
||||
}
|
||||
|
||||
if (root.contains("configFile") && root["configFile"].is_string()) {
|
||||
config.configFile = root["configFile"].get<std::string>();
|
||||
}
|
||||
|
||||
if (root.contains("targetPlatform") && root["targetPlatform"].is_number_integer()) {
|
||||
config.targetPlatform = static_cast<PlatformType>(root["targetPlatform"].get<int>());
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("JSON 应用配置加载成功");
|
||||
return ConfigLoadResult::ok();
|
||||
}
|
||||
|
||||
std::string JsonConfigLoader::saveToString(const AppConfig& config) {
|
||||
json root;
|
||||
|
||||
root["appName"] = config.appName;
|
||||
root["appVersion"] = config.appVersion;
|
||||
root["organization"] = config.organization;
|
||||
root["configFile"] = config.configFile;
|
||||
root["targetPlatform"] = static_cast<int>(config.targetPlatform);
|
||||
|
||||
return root.dump(4);
|
||||
}
|
||||
|
||||
ConfigLoadResult JsonConfigLoader::loadWithModules(const std::string& filepath) {
|
||||
E2D_LOG_INFO("正在从 JSON 文件加载完整配置(含模块): {}", filepath);
|
||||
|
||||
std::ifstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("无法打开配置文件: {}", filepath);
|
||||
return ConfigLoadResult::error("无法打开配置文件: " + filepath);
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
std::string content = buffer.str();
|
||||
file.close();
|
||||
|
||||
json root;
|
||||
try {
|
||||
root = json::parse(content);
|
||||
} catch (const json::parse_error& e) {
|
||||
E2D_LOG_ERROR("JSON 解析错误: {}", e.what());
|
||||
return ConfigLoadResult::error(std::string("JSON 解析错误: ") + e.what(),
|
||||
static_cast<int>(e.byte));
|
||||
}
|
||||
|
||||
auto& registry = ModuleRegistry::instance();
|
||||
auto moduleIds = registry.getAllModules();
|
||||
|
||||
for (ModuleId moduleId : moduleIds) {
|
||||
IModuleConfig* moduleConfig = registry.getModuleConfig(moduleId);
|
||||
if (!moduleConfig) continue;
|
||||
|
||||
std::string sectionName = moduleConfig->getConfigSectionName();
|
||||
if (sectionName.empty()) continue;
|
||||
|
||||
if (root.contains(sectionName)) {
|
||||
if (!moduleConfig->loadFromJson(&root[sectionName])) {
|
||||
E2D_LOG_WARN("模块 {} 配置加载失败", moduleConfig->getModuleInfo().name);
|
||||
} else {
|
||||
E2D_LOG_DEBUG("模块 {} 配置加载成功", moduleConfig->getModuleInfo().name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("完整配置加载成功");
|
||||
return ConfigLoadResult::ok();
|
||||
}
|
||||
|
||||
ConfigSaveResult JsonConfigLoader::saveWithModules(const std::string& filepath) {
|
||||
E2D_LOG_INFO("正在保存完整配置(含模块)到 JSON 文件: {}", filepath);
|
||||
|
||||
json root;
|
||||
|
||||
auto& registry = ModuleRegistry::instance();
|
||||
auto moduleIds = registry.getAllModules();
|
||||
|
||||
for (ModuleId moduleId : moduleIds) {
|
||||
IModuleConfig* moduleConfig = registry.getModuleConfig(moduleId);
|
||||
if (!moduleConfig) continue;
|
||||
|
||||
std::string sectionName = moduleConfig->getConfigSectionName();
|
||||
if (sectionName.empty()) continue;
|
||||
|
||||
json sectionJson;
|
||||
if (moduleConfig->saveToJson(§ionJson)) {
|
||||
root[sectionName] = sectionJson;
|
||||
}
|
||||
}
|
||||
|
||||
std::ofstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("无法创建配置文件: {}", filepath);
|
||||
return ConfigSaveResult::error("无法创建配置文件: " + filepath);
|
||||
}
|
||||
|
||||
file << root.dump(4);
|
||||
file.close();
|
||||
|
||||
E2D_LOG_INFO("完整配置已成功保存到: {}", filepath);
|
||||
return ConfigSaveResult::ok();
|
||||
}
|
||||
|
||||
bool JsonConfigLoader::supportsFile(const std::string& filepath) const {
|
||||
if (filepath.length() >= 5) {
|
||||
std::string ext = filepath.substr(filepath.length() - 5);
|
||||
for (char& c : ext) c = static_cast<char>(std::tolower(c));
|
||||
return ext == ".json";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
UniquePtr<ConfigLoader> JsonConfigLoader::clone() const {
|
||||
return makeUnique<JsonConfigLoader>();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,385 +0,0 @@
|
|||
#include <extra2d/config/config_manager.h>
|
||||
#include <extra2d/config/platform_config.h>
|
||||
#include <extra2d/config/platform_detector.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <sstream>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
ConfigManager::ConfigManager()
|
||||
: m_nextCallbackId(1)
|
||||
, m_autoSaveEnabled(false)
|
||||
, m_autoSaveInterval(30.0f)
|
||||
, m_autoSaveTimer(0.0f)
|
||||
{
|
||||
m_appConfig = AppConfig::createDefault();
|
||||
}
|
||||
|
||||
ConfigManager::~ConfigManager() {
|
||||
if (m_initialized) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
ConfigManager& ConfigManager::instance() {
|
||||
static ConfigManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool ConfigManager::initialize(const std::string& configPath) {
|
||||
if (m_initialized) {
|
||||
E2D_LOG_WARN("ConfigManager already initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
m_configPath = configPath;
|
||||
|
||||
m_platformConfig = createPlatformConfig(PlatformType::Auto);
|
||||
if (!m_platformConfig) {
|
||||
E2D_LOG_ERROR("Failed to create platform config");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_loader = makeUnique<JsonConfigLoader>();
|
||||
if (!m_loader) {
|
||||
E2D_LOG_ERROR("Failed to create config loader");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_appConfig = AppConfig::createDefault();
|
||||
m_appConfig.targetPlatform = PlatformDetector::detect();
|
||||
|
||||
m_initialized = true;
|
||||
m_modified = false;
|
||||
|
||||
E2D_LOG_INFO("ConfigManager initialized for platform: {}",
|
||||
m_platformConfig->platformName());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConfigManager::shutdown() {
|
||||
if (!m_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
if (m_autoSaveEnabled && m_modified) {
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
m_changeCallbacks.clear();
|
||||
m_rawValues.clear();
|
||||
m_loader.reset();
|
||||
m_platformConfig.reset();
|
||||
|
||||
m_initialized = false;
|
||||
m_modified = false;
|
||||
|
||||
E2D_LOG_INFO("ConfigManager shutdown complete");
|
||||
}
|
||||
|
||||
bool ConfigManager::isInitialized() const {
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
ConfigLoadResult ConfigManager::loadConfig(const std::string& filepath) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
std::string path = filepath.empty() ? m_configPath : filepath;
|
||||
if (path.empty()) {
|
||||
return ConfigLoadResult::error("No config file path specified");
|
||||
}
|
||||
|
||||
if (!m_loader) {
|
||||
return ConfigLoadResult::error("Config loader not initialized");
|
||||
}
|
||||
|
||||
AppConfig loadedConfig;
|
||||
ConfigLoadResult result = m_loader->load(path, loadedConfig);
|
||||
|
||||
if (result.success) {
|
||||
m_appConfig.merge(loadedConfig);
|
||||
|
||||
if (!m_appConfig.validate()) {
|
||||
E2D_LOG_WARN("Loaded config validation failed, using defaults");
|
||||
m_appConfig = AppConfig::createDefault();
|
||||
}
|
||||
|
||||
m_configPath = path;
|
||||
m_modified = false;
|
||||
|
||||
E2D_LOG_INFO("Config loaded from: {}", path);
|
||||
} else {
|
||||
E2D_LOG_ERROR("Failed to load config from {}: {}", path, result.errorMessage);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigSaveResult ConfigManager::saveConfig(const std::string& filepath) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
std::string path = filepath.empty() ? m_configPath : filepath;
|
||||
if (path.empty()) {
|
||||
return ConfigSaveResult::error("No config file path specified");
|
||||
}
|
||||
|
||||
if (!m_loader) {
|
||||
return ConfigSaveResult::error("Config loader not initialized");
|
||||
}
|
||||
|
||||
ConfigSaveResult result = m_loader->save(path, m_appConfig);
|
||||
|
||||
if (result.success) {
|
||||
m_configPath = path;
|
||||
m_modified = false;
|
||||
|
||||
E2D_LOG_INFO("Config saved to: {}", path);
|
||||
} else {
|
||||
E2D_LOG_ERROR("Failed to save config to {}: {}", path, result.errorMessage);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigLoadResult ConfigManager::loadConfigWithModules(const std::string& filepath) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
std::string path = filepath.empty() ? m_configPath : filepath;
|
||||
if (path.empty()) {
|
||||
return ConfigLoadResult::error("No config file path specified");
|
||||
}
|
||||
|
||||
if (!m_loader) {
|
||||
return ConfigLoadResult::error("Config loader not initialized");
|
||||
}
|
||||
|
||||
ConfigLoadResult result = m_loader->loadWithModules(path);
|
||||
|
||||
if (result.success) {
|
||||
m_configPath = path;
|
||||
m_modified = false;
|
||||
E2D_LOG_INFO("Full config (with modules) loaded from: {}", path);
|
||||
} else {
|
||||
E2D_LOG_ERROR("Failed to load full config from {}: {}", path, result.errorMessage);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigSaveResult ConfigManager::saveConfigWithModules(const std::string& filepath) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
std::string path = filepath.empty() ? m_configPath : filepath;
|
||||
if (path.empty()) {
|
||||
return ConfigSaveResult::error("No config file path specified");
|
||||
}
|
||||
|
||||
if (!m_loader) {
|
||||
return ConfigSaveResult::error("Config loader not initialized");
|
||||
}
|
||||
|
||||
ConfigSaveResult result = m_loader->saveWithModules(path);
|
||||
|
||||
if (result.success) {
|
||||
m_configPath = path;
|
||||
m_modified = false;
|
||||
E2D_LOG_INFO("Full config (with modules) saved to: {}", path);
|
||||
} else {
|
||||
E2D_LOG_ERROR("Failed to save full config to {}: {}", path, result.errorMessage);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ConfigLoadResult ConfigManager::reload() {
|
||||
return loadConfig(m_configPath);
|
||||
}
|
||||
|
||||
const AppConfig& ConfigManager::appConfig() const {
|
||||
return m_appConfig;
|
||||
}
|
||||
|
||||
AppConfig& ConfigManager::appConfig() {
|
||||
m_modified = true;
|
||||
return m_appConfig;
|
||||
}
|
||||
|
||||
void ConfigManager::setAppConfig(const AppConfig& config) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
m_appConfig = config;
|
||||
m_modified = true;
|
||||
|
||||
E2D_LOG_INFO("App config updated");
|
||||
}
|
||||
|
||||
PlatformConfig* ConfigManager::platformConfig() {
|
||||
return m_platformConfig.get();
|
||||
}
|
||||
|
||||
const PlatformConfig* ConfigManager::platformConfig() const {
|
||||
return m_platformConfig.get();
|
||||
}
|
||||
|
||||
int ConfigManager::registerChangeCallback(ConfigChangeCallback callback) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
int id = m_nextCallbackId++;
|
||||
m_changeCallbacks[id] = std::move(callback);
|
||||
|
||||
E2D_LOG_DEBUG("Registered config change callback with id {}", id);
|
||||
return id;
|
||||
}
|
||||
|
||||
void ConfigManager::unregisterChangeCallback(int callbackId) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
auto it = m_changeCallbacks.find(callbackId);
|
||||
if (it != m_changeCallbacks.end()) {
|
||||
m_changeCallbacks.erase(it);
|
||||
E2D_LOG_DEBUG("Unregistered config change callback {}", callbackId);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigManager::clearChangeCallbacks() {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_changeCallbacks.clear();
|
||||
E2D_LOG_DEBUG("Cleared all config change callbacks");
|
||||
}
|
||||
|
||||
void ConfigManager::setValue(const std::string& section, const std::string& key, const std::string& value) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
std::string fullKey = section + "." + key;
|
||||
|
||||
ConfigChangeEvent event;
|
||||
event.section = section;
|
||||
event.field = key;
|
||||
event.oldValue = m_rawValues[fullKey];
|
||||
event.newValue = value;
|
||||
|
||||
m_rawValues[fullKey] = value;
|
||||
m_modified = true;
|
||||
notifyChangeCallbacks(event);
|
||||
}
|
||||
|
||||
void ConfigManager::setValue(const std::string& section, const std::string& key, int value) {
|
||||
std::ostringstream oss;
|
||||
oss << value;
|
||||
setValue(section, key, oss.str());
|
||||
}
|
||||
|
||||
void ConfigManager::setValue(const std::string& section, const std::string& key, float value) {
|
||||
std::ostringstream oss;
|
||||
oss << value;
|
||||
setValue(section, key, oss.str());
|
||||
}
|
||||
|
||||
void ConfigManager::setValue(const std::string& section, const std::string& key, bool value) {
|
||||
setValue(section, key, std::string(value ? "true" : "false"));
|
||||
}
|
||||
|
||||
std::string ConfigManager::getValue(const std::string& section, const std::string& key,
|
||||
const std::string& defaultValue) const {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
std::string fullKey = section + "." + key;
|
||||
auto it = m_rawValues.find(fullKey);
|
||||
if (it != m_rawValues.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
int ConfigManager::getIntValue(const std::string& section, const std::string& key, int defaultValue) const {
|
||||
std::string value = getValue(section, key);
|
||||
if (!value.empty()) {
|
||||
try {
|
||||
return std::stoi(value);
|
||||
} catch (...) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
float ConfigManager::getFloatValue(const std::string& section, const std::string& key, float defaultValue) const {
|
||||
std::string value = getValue(section, key);
|
||||
if (!value.empty()) {
|
||||
try {
|
||||
return std::stof(value);
|
||||
} catch (...) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
bool ConfigManager::getBoolValue(const std::string& section, const std::string& key, bool defaultValue) const {
|
||||
std::string value = getValue(section, key);
|
||||
if (!value.empty()) {
|
||||
if (value == "true" || value == "1") return true;
|
||||
if (value == "false" || value == "0") return false;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
void ConfigManager::resetToDefaults() {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
m_appConfig = AppConfig::createDefault();
|
||||
m_rawValues.clear();
|
||||
m_modified = true;
|
||||
|
||||
E2D_LOG_INFO("Config reset to defaults");
|
||||
}
|
||||
|
||||
bool ConfigManager::hasUnsavedChanges() const {
|
||||
return m_modified;
|
||||
}
|
||||
|
||||
void ConfigManager::markModified() {
|
||||
m_modified = true;
|
||||
}
|
||||
|
||||
void ConfigManager::clearModified() {
|
||||
m_modified = false;
|
||||
}
|
||||
|
||||
void ConfigManager::setAutoSave(bool enabled, float interval) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
m_autoSaveEnabled = enabled;
|
||||
m_autoSaveInterval = interval > 0.0f ? interval : 30.0f;
|
||||
m_autoSaveTimer = 0.0f;
|
||||
|
||||
E2D_LOG_INFO("Auto save {} (interval: {}s)",
|
||||
enabled ? "enabled" : "disabled", m_autoSaveInterval);
|
||||
}
|
||||
|
||||
void ConfigManager::update(float deltaTime) {
|
||||
if (!m_autoSaveEnabled || !m_modified) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_autoSaveTimer += deltaTime;
|
||||
|
||||
if (m_autoSaveTimer >= m_autoSaveInterval) {
|
||||
m_autoSaveTimer = 0.0f;
|
||||
saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigManager::notifyChangeCallbacks(const ConfigChangeEvent& event) {
|
||||
for (const auto& pair : m_changeCallbacks) {
|
||||
if (pair.second) {
|
||||
pair.second(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
#include <extra2d/config/config_module.h>
|
||||
#include <extra2d/config/module_registry.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
static ModuleId s_configModuleId = INVALID_MODULE_ID;
|
||||
|
||||
ModuleId get_config_module_id() {
|
||||
return s_configModuleId;
|
||||
}
|
||||
|
||||
bool ConfigModuleConfig::loadFromJson(const void* jsonData) {
|
||||
if (!jsonData) return false;
|
||||
|
||||
try {
|
||||
const json& j = *static_cast<const json*>(jsonData);
|
||||
|
||||
if (j.contains("configPath")) {
|
||||
configPath = j["configPath"].get<std::string>();
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigModuleConfig::saveToJson(void* jsonData) const {
|
||||
if (!jsonData) return false;
|
||||
|
||||
try {
|
||||
json& j = *static_cast<json*>(jsonData);
|
||||
j["configPath"] = configPath;
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigModuleInitializer::ConfigModuleInitializer()
|
||||
: moduleId_(INVALID_MODULE_ID)
|
||||
, initialized_(false) {
|
||||
}
|
||||
|
||||
ConfigModuleInitializer::~ConfigModuleInitializer() {
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigModuleInitializer::initialize(const IModuleConfig* config) {
|
||||
if (initialized_) return true;
|
||||
|
||||
const ConfigModuleConfig* configModule = dynamic_cast<const ConfigModuleConfig*>(config);
|
||||
|
||||
if (!configPath_.empty()) {
|
||||
if (!ConfigManager::instance().initialize(configPath_)) {
|
||||
if (!ConfigManager::instance().initialize()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!ConfigManager::instance().initialize()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (configModule && !configModule->appConfig.appName.empty()) {
|
||||
ConfigManager::instance().setAppConfig(configModule->appConfig);
|
||||
} else if (!appConfig_.appName.empty()) {
|
||||
ConfigManager::instance().setAppConfig(appConfig_);
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("Config module initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConfigModuleInitializer::shutdown() {
|
||||
if (!initialized_) return;
|
||||
|
||||
E2D_LOG_INFO("Config module shutting down");
|
||||
ConfigManager::instance().shutdown();
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
void register_config_module() {
|
||||
if (s_configModuleId != INVALID_MODULE_ID) return;
|
||||
|
||||
s_configModuleId = ModuleRegistry::instance().registerModule(
|
||||
makeUnique<ConfigModuleConfig>(),
|
||||
[]() -> UniquePtr<IModuleInitializer> {
|
||||
auto initializer = makeUnique<ConfigModuleInitializer>();
|
||||
initializer->setModuleId(s_configModuleId);
|
||||
return initializer;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct ConfigModuleAutoRegister {
|
||||
ConfigModuleAutoRegister() {
|
||||
register_config_module();
|
||||
}
|
||||
};
|
||||
|
||||
static ConfigModuleAutoRegister s_autoRegister;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,211 +0,0 @@
|
|||
#include <extra2d/config/module_registry.h>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* 使用静态局部变量实现线程安全的单例模式
|
||||
* @return 模块注册表实例引用
|
||||
*/
|
||||
ModuleRegistry& ModuleRegistry::instance() {
|
||||
static ModuleRegistry instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册模块
|
||||
* 将模块配置和初始化器工厂添加到注册表
|
||||
* @param config 模块配置
|
||||
* @param initializerFactory 初始化器工厂函数(可选)
|
||||
* @return 分配的模块标识符
|
||||
*/
|
||||
ModuleId ModuleRegistry::registerModule(
|
||||
UniquePtr<IModuleConfig> config,
|
||||
ModuleInitializerFactory initializerFactory
|
||||
) {
|
||||
if (!config) {
|
||||
std::fprintf(stderr, "[ERROR] Cannot register null module config\n");
|
||||
return INVALID_MODULE_ID;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
ModuleInfo info = config->getModuleInfo();
|
||||
|
||||
if (nameToId_.find(info.name) != nameToId_.end()) {
|
||||
std::fprintf(stderr, "[ERROR] Module '%s' already registered\n", info.name.c_str());
|
||||
return INVALID_MODULE_ID;
|
||||
}
|
||||
|
||||
ModuleId id = generateId();
|
||||
|
||||
ModuleEntry entry;
|
||||
entry.id = id;
|
||||
entry.config = std::move(config);
|
||||
entry.initializerFactory = std::move(initializerFactory);
|
||||
entry.initialized = false;
|
||||
|
||||
modules_[id] = std::move(entry);
|
||||
nameToId_[info.name] = id;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注销模块
|
||||
* 从注册表中移除指定模块
|
||||
* @param id 模块标识符
|
||||
* @return 注销成功返回 true
|
||||
*/
|
||||
bool ModuleRegistry::unregisterModule(ModuleId id) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto it = modules_.find(id);
|
||||
if (it == modules_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ModuleInfo info = it->second.config->getModuleInfo();
|
||||
nameToId_.erase(info.name);
|
||||
modules_.erase(it);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取模块配置
|
||||
* @param id 模块标识符
|
||||
* @return 模块配置指针,不存在返回 nullptr
|
||||
*/
|
||||
IModuleConfig* ModuleRegistry::getModuleConfig(ModuleId id) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto it = modules_.find(id);
|
||||
if (it != modules_.end()) {
|
||||
return it->second.config.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据名称获取模块配置
|
||||
* @param name 模块名称
|
||||
* @return 模块配置指针,不存在返回 nullptr
|
||||
*/
|
||||
IModuleConfig* ModuleRegistry::getModuleConfigByName(const std::string& name) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto nameIt = nameToId_.find(name);
|
||||
if (nameIt == nameToId_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto moduleIt = modules_.find(nameIt->second);
|
||||
if (moduleIt != modules_.end()) {
|
||||
return moduleIt->second.config.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取或创建模块初始化器
|
||||
* @param id 模块标识符
|
||||
* @return 初始化器指针,不存在返回 nullptr
|
||||
*/
|
||||
IModuleInitializer* ModuleRegistry::getInitializer(ModuleId id) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto it = modules_.find(id);
|
||||
if (it == modules_.end() || !it->second.initializerFactory) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!it->second.initializer) {
|
||||
it->second.initializer = it->second.initializerFactory();
|
||||
}
|
||||
|
||||
return it->second.initializer.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取所有已注册模块标识符
|
||||
* @return 模块标识符列表
|
||||
*/
|
||||
std::vector<ModuleId> ModuleRegistry::getAllModules() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
std::vector<ModuleId> result;
|
||||
result.reserve(modules_.size());
|
||||
|
||||
for (const auto& pair : modules_) {
|
||||
result.push_back(pair.first);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取模块初始化顺序
|
||||
* 根据优先级和依赖关系计算初始化顺序
|
||||
* @return 按初始化顺序排列的模块标识符列表
|
||||
*/
|
||||
std::vector<ModuleId> ModuleRegistry::getInitializationOrder() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
std::vector<std::pair<ModuleId, int>> modulePriorities;
|
||||
modulePriorities.reserve(modules_.size());
|
||||
|
||||
for (const auto& pair : modules_) {
|
||||
ModuleInfo info = pair.second.config->getModuleInfo();
|
||||
if (info.enabled) {
|
||||
modulePriorities.emplace_back(pair.first, static_cast<int>(info.priority));
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(modulePriorities.begin(), modulePriorities.end(),
|
||||
[](const auto& a, const auto& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
|
||||
std::vector<ModuleId> result;
|
||||
result.reserve(modulePriorities.size());
|
||||
|
||||
for (const auto& pair : modulePriorities) {
|
||||
result.push_back(pair.first);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查模块是否存在
|
||||
* @param id 模块标识符
|
||||
* @return 存在返回 true
|
||||
*/
|
||||
bool ModuleRegistry::hasModule(ModuleId id) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return modules_.find(id) != modules_.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空所有注册的模块
|
||||
*/
|
||||
void ModuleRegistry::clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
modules_.clear();
|
||||
nameToId_.clear();
|
||||
nextId_ = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 生成新的模块标识符
|
||||
* @return 新的模块标识符
|
||||
*/
|
||||
ModuleId ModuleRegistry::generateId() {
|
||||
return nextId_++;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
#include <extra2d/core/registry.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
#include <iostream>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Registry& Registry::instance() {
|
||||
static Registry instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool Registry::init() {
|
||||
auto sorted = topologicalSort();
|
||||
|
||||
std::cout << "[Registry] Initializing " << sorted.size() << " modules..." << std::endl;
|
||||
|
||||
for (auto* module : sorted) {
|
||||
std::cout << "[Registry] Initializing module: " << module->name() << std::endl;
|
||||
if (!module->init()) {
|
||||
std::cerr << "[Registry] Failed to initialize module: " << module->name() << std::endl;
|
||||
return false;
|
||||
}
|
||||
std::cout << "[Registry] Module " << module->name() << " initialized successfully" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "[Registry] All modules initialized" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Registry::shutdown() {
|
||||
auto sorted = topologicalSort();
|
||||
|
||||
// 反向关闭
|
||||
for (auto it = sorted.rbegin(); it != sorted.rend(); ++it) {
|
||||
(*it)->shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void Registry::clear() {
|
||||
shutdown();
|
||||
modules_.clear();
|
||||
}
|
||||
|
||||
std::vector<Module*> Registry::topologicalSort() {
|
||||
std::vector<Module*> result;
|
||||
std::unordered_map<Module*, int> inDegree;
|
||||
std::unordered_map<Module*, std::vector<Module*>> adj;
|
||||
|
||||
// 构建图
|
||||
for (auto& [typeIdx, module] : modules_) {
|
||||
inDegree[module.get()] = 0;
|
||||
}
|
||||
|
||||
for (auto& [typeIdx, module] : modules_) {
|
||||
for (auto& depType : module->deps()) {
|
||||
Module* dep = get(depType);
|
||||
if (dep) {
|
||||
adj[dep].push_back(module.get());
|
||||
inDegree[module.get()]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 优先级队列(优先级小的先处理)
|
||||
auto cmp = [](Module* a, Module* b) {
|
||||
return a->priority() > b->priority();
|
||||
};
|
||||
std::priority_queue<Module*, std::vector<Module*>, decltype(cmp)> pq(cmp);
|
||||
|
||||
for (auto& [mod, degree] : inDegree) {
|
||||
if (degree == 0) {
|
||||
pq.push(mod);
|
||||
}
|
||||
}
|
||||
|
||||
// 拓扑排序
|
||||
while (!pq.empty()) {
|
||||
Module* curr = pq.top();
|
||||
pq.pop();
|
||||
result.push_back(curr);
|
||||
|
||||
for (Module* next : adj[curr]) {
|
||||
inDegree[next]--;
|
||||
if (inDegree[next] == 0) {
|
||||
pq.push(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
#include <extra2d/debug/debug_config.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
bool DebugConfigData::hasDebugFlag(const std::string& flag) const {
|
||||
return std::find(debugFlags.begin(), debugFlags.end(), flag) != debugFlags.end();
|
||||
}
|
||||
|
||||
void DebugConfigData::addDebugFlag(const std::string& flag) {
|
||||
if (!hasDebugFlag(flag)) {
|
||||
debugFlags.push_back(flag);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugConfigData::removeDebugFlag(const std::string& flag) {
|
||||
auto it = std::find(debugFlags.begin(), debugFlags.end(), flag);
|
||||
if (it != debugFlags.end()) {
|
||||
debugFlags.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#include <algorithm>
|
||||
#include <extra2d/graphics/camera.h>
|
||||
#include <extra2d/graphics/viewport_adapter.h>
|
||||
#include <extra2d/graphics/camera/camera.h>
|
||||
#include <extra2d/graphics/camera/viewport_adapter.h>
|
||||
#include <glm/gtc/matrix_inverse.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#include <extra2d/graphics/viewport_adapter.h>
|
||||
#include <extra2d/graphics/camera/viewport_adapter.h>
|
||||
#include <algorithm>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#include <extra2d/graphics/opengl/gl_renderer.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#include <extra2d/graphics/render_command.h>
|
||||
#include <extra2d/graphics/core/render_command.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
#include <extra2d/graphics/core/render_module.h>
|
||||
#include <extra2d/graphics/opengl/gl_renderer.h>
|
||||
#include <extra2d/graphics/opengl/gl_shader.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/core/registry.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/app/application.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <SDL.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
RenderModule::RenderModule(const Cfg& cfg) : cfg_(cfg) {}
|
||||
|
||||
RenderModule::~RenderModule() {
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
static std::string getExecutableDir() {
|
||||
char* basePath = SDL_GetBasePath();
|
||||
if (basePath) {
|
||||
std::string path(basePath);
|
||||
SDL_free(basePath);
|
||||
return path;
|
||||
}
|
||||
return "./";
|
||||
}
|
||||
|
||||
bool RenderModule::init() {
|
||||
if (initialized_) return true;
|
||||
|
||||
// 获取WindowModule依赖
|
||||
auto* winMod = Registry::instance().get<WindowModule>();
|
||||
if (!winMod || !winMod->win()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 初始化ShaderManager
|
||||
if (!ShaderManager::getInstance().isInitialized()) {
|
||||
auto factory = makeShared<GLShaderFactory>();
|
||||
std::string shaderDir = getExecutableDir() + "shaders/";
|
||||
std::string cacheDir = getExecutableDir() + "shader_cache/";
|
||||
if (!ShaderManager::getInstance().init(shaderDir, cacheDir, factory)) {
|
||||
E2D_LOG_WARN("Failed to initialize ShaderManager with dir: {}", shaderDir);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建渲染后端
|
||||
renderer_ = RenderBackend::create(cfg_.backend);
|
||||
if (!renderer_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 初始化渲染器
|
||||
if (!renderer_->init(winMod->win())) {
|
||||
renderer_.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderModule::shutdown() {
|
||||
if (!initialized_) return;
|
||||
|
||||
if (renderer_) {
|
||||
renderer_->shutdown();
|
||||
renderer_.reset();
|
||||
}
|
||||
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#include <glad/glad.h>
|
||||
#include <extra2d/graphics/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/render_target.h>
|
||||
#include <extra2d/graphics/core/render_target.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#include <extra2d/graphics/gpu_context.h>
|
||||
#include <extra2d/graphics/memory/gpu_context.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#include <algorithm>
|
||||
#include <extra2d/graphics/vram_manager.h>
|
||||
#include <extra2d/graphics/memory/vram_manager.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -2,12 +2,12 @@
|
|||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <extra2d/graphics/gpu_context.h>
|
||||
#include <extra2d/graphics/memory/gpu_context.h>
|
||||
#include <extra2d/graphics/opengl/gl_font_atlas.h>
|
||||
#include <extra2d/graphics/opengl/gl_renderer.h>
|
||||
#include <extra2d/graphics/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/shader_manager.h>
|
||||
#include <extra2d/graphics/vram_manager.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/graphics/memory/vram_manager.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <vector>
|
||||
|
|
@ -633,7 +633,7 @@ void GLRenderer::resetStats() { stats_ = Stats{}; }
|
|||
*/
|
||||
void GLRenderer::initShapeRendering() {
|
||||
// 从ShaderManager获取形状着色器
|
||||
shapeShader_ = ShaderManager::getInstance().getBuiltin("builtin_shape");
|
||||
shapeShader_ = ShaderManager::getInstance().getBuiltin("shape");
|
||||
if (!shapeShader_) {
|
||||
E2D_LOG_WARN("Failed to get builtin shape shader, loading from manager");
|
||||
if (!ShaderManager::getInstance().isInitialized()) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#include <extra2d/graphics/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/gpu_context.h>
|
||||
#include <extra2d/graphics/vram_manager.h>
|
||||
#include <extra2d/graphics/memory/gpu_context.h>
|
||||
#include <extra2d/graphics/memory/vram_manager.h>
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <cstring>
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
|
|
|||
|
|
@ -1,214 +0,0 @@
|
|||
#include <extra2d/graphics/render_module.h>
|
||||
#include <extra2d/config/module_registry.h>
|
||||
#include <extra2d/graphics/opengl/gl_shader.h>
|
||||
#include <extra2d/graphics/shader_manager.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
static ModuleId s_renderModuleId = INVALID_MODULE_ID;
|
||||
|
||||
ModuleId get_render_module_id() {
|
||||
return s_renderModuleId;
|
||||
}
|
||||
|
||||
bool RenderModuleConfig::validate() const {
|
||||
if (targetFPS < 1 || targetFPS > 240) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (multisamples != 0 && multisamples != 2 && multisamples != 4 &&
|
||||
multisamples != 8 && multisamples != 16) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spriteBatchSize <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderModuleConfig::applyPlatformConstraints(PlatformType platform) {
|
||||
switch (platform) {
|
||||
case PlatformType::Switch:
|
||||
if (multisamples > 4) {
|
||||
multisamples = 4;
|
||||
}
|
||||
if (sRGBFramebuffer) {
|
||||
sRGBFramebuffer = false;
|
||||
}
|
||||
if (targetFPS > 60) {
|
||||
targetFPS = 60;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderModuleConfig::resetToDefaults() {
|
||||
backend = BackendType::OpenGL;
|
||||
vsync = true;
|
||||
targetFPS = 60;
|
||||
multisamples = 0;
|
||||
sRGBFramebuffer = false;
|
||||
spriteBatchSize = 1000;
|
||||
}
|
||||
|
||||
bool RenderModuleConfig::loadFromJson(const void* jsonData) {
|
||||
if (!jsonData) return false;
|
||||
|
||||
try {
|
||||
const json& j = *static_cast<const json*>(jsonData);
|
||||
|
||||
if (j.contains("backend")) {
|
||||
std::string backendStr = j["backend"].get<std::string>();
|
||||
if (backendStr == "opengl") {
|
||||
backend = BackendType::OpenGL;
|
||||
}
|
||||
}
|
||||
|
||||
if (j.contains("vsync")) {
|
||||
vsync = j["vsync"].get<bool>();
|
||||
}
|
||||
|
||||
if (j.contains("targetFPS")) {
|
||||
targetFPS = j["targetFPS"].get<int>();
|
||||
}
|
||||
|
||||
if (j.contains("multisamples")) {
|
||||
multisamples = j["multisamples"].get<int>();
|
||||
}
|
||||
|
||||
if (j.contains("sRGBFramebuffer")) {
|
||||
sRGBFramebuffer = j["sRGBFramebuffer"].get<bool>();
|
||||
}
|
||||
|
||||
if (j.contains("spriteBatchSize")) {
|
||||
spriteBatchSize = j["spriteBatchSize"].get<int>();
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderModuleConfig::saveToJson(void* jsonData) const {
|
||||
if (!jsonData) return false;
|
||||
|
||||
try {
|
||||
json& j = *static_cast<json*>(jsonData);
|
||||
j["backend"] = "opengl";
|
||||
j["vsync"] = vsync;
|
||||
j["targetFPS"] = targetFPS;
|
||||
j["multisamples"] = multisamples;
|
||||
j["sRGBFramebuffer"] = sRGBFramebuffer;
|
||||
j["spriteBatchSize"] = spriteBatchSize;
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RenderModuleInitializer::RenderModuleInitializer()
|
||||
: moduleId_(INVALID_MODULE_ID)
|
||||
, window_(nullptr)
|
||||
, initialized_(false) {
|
||||
}
|
||||
|
||||
RenderModuleInitializer::~RenderModuleInitializer() {
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ModuleId> RenderModuleInitializer::getDependencies() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool RenderModuleInitializer::initialize(const IModuleConfig* config) {
|
||||
if (initialized_) return true;
|
||||
|
||||
if (!config) return false;
|
||||
|
||||
const RenderModuleConfig* renderConfig = dynamic_cast<const RenderModuleConfig*>(config);
|
||||
if (!renderConfig) return false;
|
||||
|
||||
if (!renderConfig->validate()) return false;
|
||||
|
||||
if (!window_) {
|
||||
E2D_LOG_ERROR("Render module requires window to be set");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto shaderFactory = std::make_shared<GLShaderFactory>();
|
||||
if (!ShaderManager::getInstance().init(shaderFactory, "extra2d")) {
|
||||
E2D_LOG_WARN("Failed to initialize ShaderManager with default paths");
|
||||
}
|
||||
|
||||
if (!ShaderManager::getInstance().loadBuiltinShaders()) {
|
||||
E2D_LOG_WARN("Failed to load some builtin shaders");
|
||||
}
|
||||
|
||||
renderer_ = RenderBackend::create(renderConfig->backend);
|
||||
if (!renderer_) {
|
||||
E2D_LOG_ERROR("Failed to create render backend");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!renderer_->init(window_)) {
|
||||
E2D_LOG_ERROR("Failed to initialize renderer");
|
||||
renderer_.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("Render module initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderModuleInitializer::shutdown() {
|
||||
if (!initialized_) return;
|
||||
|
||||
if (renderer_) {
|
||||
renderer_->shutdown();
|
||||
renderer_.reset();
|
||||
}
|
||||
|
||||
ShaderManager::getInstance().shutdown();
|
||||
|
||||
initialized_ = false;
|
||||
E2D_LOG_INFO("Render module shutdown");
|
||||
}
|
||||
|
||||
void register_render_module() {
|
||||
if (s_renderModuleId != INVALID_MODULE_ID) return;
|
||||
|
||||
s_renderModuleId = ModuleRegistry::instance().registerModule(
|
||||
makeUnique<RenderModuleConfig>(),
|
||||
[]() -> UniquePtr<IModuleInitializer> {
|
||||
auto initializer = makeUnique<RenderModuleInitializer>();
|
||||
initializer->setModuleId(s_renderModuleId);
|
||||
return initializer;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct RenderModuleAutoRegister {
|
||||
RenderModuleAutoRegister() {
|
||||
register_render_module();
|
||||
}
|
||||
};
|
||||
|
||||
static RenderModuleAutoRegister s_autoRegister;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#include <extra2d/graphics/shader_cache.h>
|
||||
#include <extra2d/graphics/shader/shader_cache.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#include <extra2d/graphics/shader_hot_reloader.h>
|
||||
#include <extra2d/graphics/shader/shader_hot_reloader.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#include <extra2d/graphics/shader_loader.h>
|
||||
#include <extra2d/graphics/shader/shader_loader.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#include <extra2d/graphics/shader_manager.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#include <extra2d/graphics/shader_manager.h>
|
||||
#include <extra2d/graphics/shader_preset.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/graphics/shader/shader_preset.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#include <extra2d/graphics/alpha_mask.h>
|
||||
#include <extra2d/graphics/texture/alpha_mask.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#include <extra2d/graphics/texture_atlas.h>
|
||||
#include <extra2d/graphics/texture/texture_atlas.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#include <extra2d/graphics/texture_pool.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/texture/texture_pool.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/scene/scene.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#include <extra2d/platform/platform_module.h>
|
||||
#include <extra2d/platform/backend_factory.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#include "sdl2_window.h"
|
||||
#include "sdl2_input.h"
|
||||
#include <extra2d/platform/platform_module.h>
|
||||
#include <extra2d/platform/backend_factory.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,213 +1,49 @@
|
|||
#include <extra2d/platform/input_module.h>
|
||||
#include <extra2d/config/module_registry.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/core/registry.h>
|
||||
#include <extra2d/services/event_service.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "backends/sdl2/sdl2_input.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
#include <extra2d/platform/window_module.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
static ModuleId s_inputModuleId = INVALID_MODULE_ID;
|
||||
InputModule::InputModule(const Cfg& cfg) : cfg_(cfg) {}
|
||||
|
||||
ModuleId get_input_module_id() {
|
||||
return s_inputModuleId;
|
||||
}
|
||||
|
||||
bool InputModuleConfig::validate() const {
|
||||
return inputConfig.isDeadzoneValid();
|
||||
}
|
||||
|
||||
void InputModuleConfig::applyPlatformConstraints(PlatformType platform) {
|
||||
#ifdef __SWITCH__
|
||||
(void)platform;
|
||||
inputConfig.enableVibration = true;
|
||||
inputConfig.maxGamepads = 2;
|
||||
#else
|
||||
(void)platform;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool InputModuleConfig::loadFromJson(const void* jsonData) {
|
||||
if (!jsonData) return false;
|
||||
|
||||
try {
|
||||
const json& j = *static_cast<const json*>(jsonData);
|
||||
|
||||
if (j.contains("enabled")) {
|
||||
inputConfig.enabled = j["enabled"].get<bool>();
|
||||
}
|
||||
if (j.contains("rawMouseInput")) {
|
||||
inputConfig.rawMouseInput = j["rawMouseInput"].get<bool>();
|
||||
}
|
||||
if (j.contains("mouseSensitivity")) {
|
||||
inputConfig.mouseSensitivity = j["mouseSensitivity"].get<float>();
|
||||
}
|
||||
if (j.contains("invertMouseY")) {
|
||||
inputConfig.invertMouseY = j["invertMouseY"].get<bool>();
|
||||
}
|
||||
if (j.contains("invertMouseX")) {
|
||||
inputConfig.invertMouseX = j["invertMouseX"].get<bool>();
|
||||
}
|
||||
if (j.contains("deadzone")) {
|
||||
inputConfig.deadzone = j["deadzone"].get<float>();
|
||||
}
|
||||
if (j.contains("triggerThreshold")) {
|
||||
inputConfig.triggerThreshold = j["triggerThreshold"].get<float>();
|
||||
}
|
||||
if (j.contains("enableVibration")) {
|
||||
inputConfig.enableVibration = j["enableVibration"].get<bool>();
|
||||
}
|
||||
if (j.contains("maxGamepads")) {
|
||||
inputConfig.maxGamepads = j["maxGamepads"].get<int>();
|
||||
}
|
||||
if (j.contains("autoConnectGamepads")) {
|
||||
inputConfig.autoConnectGamepads = j["autoConnectGamepads"].get<bool>();
|
||||
}
|
||||
if (j.contains("gamepadMappingFile")) {
|
||||
inputConfig.gamepadMappingFile = j["gamepadMappingFile"].get<std::string>();
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool InputModuleConfig::saveToJson(void* jsonData) const {
|
||||
if (!jsonData) return false;
|
||||
|
||||
try {
|
||||
json& j = *static_cast<json*>(jsonData);
|
||||
|
||||
j["enabled"] = inputConfig.enabled;
|
||||
j["rawMouseInput"] = inputConfig.rawMouseInput;
|
||||
j["mouseSensitivity"] = inputConfig.mouseSensitivity;
|
||||
j["invertMouseY"] = inputConfig.invertMouseY;
|
||||
j["invertMouseX"] = inputConfig.invertMouseX;
|
||||
j["deadzone"] = inputConfig.deadzone;
|
||||
j["triggerThreshold"] = inputConfig.triggerThreshold;
|
||||
j["enableVibration"] = inputConfig.enableVibration;
|
||||
j["maxGamepads"] = inputConfig.maxGamepads;
|
||||
j["autoConnectGamepads"] = inputConfig.autoConnectGamepads;
|
||||
j["gamepadMappingFile"] = inputConfig.gamepadMappingFile;
|
||||
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
InputModuleInitializer::InputModuleInitializer()
|
||||
: moduleId_(INVALID_MODULE_ID)
|
||||
, input_(nullptr)
|
||||
, initialized_(false) {
|
||||
}
|
||||
|
||||
InputModuleInitializer::~InputModuleInitializer() {
|
||||
InputModule::~InputModule() {
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ModuleId> InputModuleInitializer::getDependencies() const {
|
||||
return { get_window_module_id() };
|
||||
}
|
||||
|
||||
bool InputModuleInitializer::initialize(const IModuleConfig* config) {
|
||||
bool InputModule::init() {
|
||||
if (initialized_) return true;
|
||||
|
||||
const InputModuleConfig* inputConfig = dynamic_cast<const InputModuleConfig*>(config);
|
||||
if (!inputConfig) {
|
||||
E2D_LOG_ERROR("Invalid input module config");
|
||||
// 获取WindowModule依赖
|
||||
auto* winMod = Registry::instance().get<WindowModule>();
|
||||
if (!winMod || !winMod->win()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
config_ = inputConfig->inputConfig;
|
||||
|
||||
auto& registry = ModuleRegistry::instance();
|
||||
auto* windowInitializer = registry.getInitializer(get_window_module_id());
|
||||
if (!windowInitializer) {
|
||||
E2D_LOG_ERROR("Window module not found - Input module depends on it");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* windowModule = static_cast<WindowModuleInitializer*>(windowInitializer);
|
||||
IWindow* window = windowModule->getWindow();
|
||||
if (!window) {
|
||||
E2D_LOG_ERROR("Window not created - cannot get input interface");
|
||||
return false;
|
||||
}
|
||||
|
||||
input_ = window->input();
|
||||
// 获取输入接口
|
||||
input_ = winMod->win()->input();
|
||||
if (!input_) {
|
||||
E2D_LOG_ERROR("Input interface not available from window");
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL2Input* sdl2Input = dynamic_cast<SDL2Input*>(input_);
|
||||
if (sdl2Input) {
|
||||
auto eventService = ServiceLocator::instance().getService<IEventService>();
|
||||
if (eventService) {
|
||||
sdl2Input->setEventCallback([eventService](const Event& event) {
|
||||
Event mutableEvent = event;
|
||||
eventService->dispatch(mutableEvent);
|
||||
});
|
||||
E2D_LOG_INFO("Input events connected to EventService");
|
||||
} else {
|
||||
E2D_LOG_WARN("EventService not available - input events will not be dispatched");
|
||||
}
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("Input module initialized");
|
||||
E2D_LOG_INFO(" Deadzone: {}", config_.deadzone);
|
||||
E2D_LOG_INFO(" Mouse sensitivity: {}", config_.mouseSensitivity);
|
||||
E2D_LOG_INFO(" Vibration: {}", config_.enableVibration ? "enabled" : "disabled");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InputModuleInitializer::shutdown() {
|
||||
void InputModule::shutdown() {
|
||||
if (!initialized_) return;
|
||||
|
||||
E2D_LOG_INFO("Input module shutting down");
|
||||
|
||||
input_ = nullptr;
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
void InputModuleInitializer::update() {
|
||||
if (!initialized_ || !input_) return;
|
||||
|
||||
input_->update();
|
||||
void InputModule::update() {
|
||||
if (initialized_ && input_) {
|
||||
input_->update();
|
||||
}
|
||||
}
|
||||
|
||||
void register_input_module() {
|
||||
if (s_inputModuleId != INVALID_MODULE_ID) return;
|
||||
|
||||
s_inputModuleId = ModuleRegistry::instance().registerModule(
|
||||
makeUnique<InputModuleConfig>(),
|
||||
[]() -> UniquePtr<IModuleInitializer> {
|
||||
auto initializer = makeUnique<InputModuleInitializer>();
|
||||
initializer->setModuleId(s_inputModuleId);
|
||||
return initializer;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct InputModuleAutoRegister {
|
||||
InputModuleAutoRegister() {
|
||||
register_input_module();
|
||||
}
|
||||
};
|
||||
|
||||
static InputModuleAutoRegister s_autoRegister;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,168 +0,0 @@
|
|||
#include <extra2d/platform/platform_init_module.h>
|
||||
#include <extra2d/config/module_registry.h>
|
||||
#include <extra2d/config/config_manager.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#include <switch.h>
|
||||
#endif
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
static ModuleId s_platformModuleId = INVALID_MODULE_ID;
|
||||
|
||||
ModuleId get_platform_module_id() {
|
||||
return s_platformModuleId;
|
||||
}
|
||||
|
||||
bool PlatformModuleConfig::loadFromJson(const void* jsonData) {
|
||||
if (!jsonData) return false;
|
||||
|
||||
try {
|
||||
const json& j = *static_cast<const json*>(jsonData);
|
||||
|
||||
if (j.contains("targetPlatform")) {
|
||||
int platform = j["targetPlatform"].get<int>();
|
||||
if (platform >= 0 && platform <= 5) {
|
||||
targetPlatform = static_cast<PlatformType>(platform);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PlatformModuleConfig::saveToJson(void* jsonData) const {
|
||||
if (!jsonData) return false;
|
||||
|
||||
try {
|
||||
json& j = *static_cast<json*>(jsonData);
|
||||
j["targetPlatform"] = static_cast<int>(targetPlatform);
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
PlatformModuleInitializer::PlatformModuleInitializer()
|
||||
: moduleId_(INVALID_MODULE_ID)
|
||||
, initialized_(false)
|
||||
, targetPlatform_(PlatformType::Auto)
|
||||
, resolvedPlatform_(PlatformType::Windows) {
|
||||
}
|
||||
|
||||
PlatformModuleInitializer::~PlatformModuleInitializer() {
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool PlatformModuleInitializer::initialize(const IModuleConfig* config) {
|
||||
if (initialized_) return true;
|
||||
|
||||
const PlatformModuleConfig* platformConfig = dynamic_cast<const PlatformModuleConfig*>(config);
|
||||
if (platformConfig) {
|
||||
targetPlatform_ = platformConfig->targetPlatform;
|
||||
}
|
||||
|
||||
resolvedPlatform_ = targetPlatform_;
|
||||
if (resolvedPlatform_ == PlatformType::Auto) {
|
||||
#ifdef __SWITCH__
|
||||
resolvedPlatform_ = PlatformType::Switch;
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
resolvedPlatform_ = PlatformType::Windows;
|
||||
#elif defined(__linux__)
|
||||
resolvedPlatform_ = PlatformType::Linux;
|
||||
#elif defined(__APPLE__)
|
||||
resolvedPlatform_ = PlatformType::macOS;
|
||||
#else
|
||||
resolvedPlatform_ = PlatformType::Windows;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
platformConfig_ = createPlatformConfig(resolvedPlatform_);
|
||||
if (!platformConfig_) {
|
||||
E2D_LOG_ERROR("Failed to create platform config");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resolvedPlatform_ == PlatformType::Switch) {
|
||||
if (!initSwitch()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("Platform module initialized ({})", getPlatformTypeName(resolvedPlatform_));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlatformModuleInitializer::initSwitch() {
|
||||
#ifdef __SWITCH__
|
||||
Result rc;
|
||||
rc = romfsInit();
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
E2D_LOG_INFO("RomFS initialized successfully");
|
||||
} else {
|
||||
E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem", rc);
|
||||
}
|
||||
|
||||
rc = socketInitializeDefault();
|
||||
if (R_FAILED(rc)) {
|
||||
E2D_LOG_WARN("socketInitializeDefault failed, nxlink will not be available");
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformModuleInitializer::shutdown() {
|
||||
if (!initialized_) return;
|
||||
|
||||
E2D_LOG_INFO("Platform module shutting down");
|
||||
|
||||
if (resolvedPlatform_ == PlatformType::Switch) {
|
||||
shutdownSwitch();
|
||||
}
|
||||
|
||||
platformConfig_.reset();
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
void PlatformModuleInitializer::shutdownSwitch() {
|
||||
#ifdef __SWITCH__
|
||||
romfsExit();
|
||||
socketExit();
|
||||
#endif
|
||||
}
|
||||
|
||||
void register_platform_module() {
|
||||
if (s_platformModuleId != INVALID_MODULE_ID) return;
|
||||
|
||||
s_platformModuleId = ModuleRegistry::instance().registerModule(
|
||||
makeUnique<PlatformModuleConfig>(),
|
||||
[]() -> UniquePtr<IModuleInitializer> {
|
||||
auto initializer = makeUnique<PlatformModuleInitializer>();
|
||||
initializer->setModuleId(s_platformModuleId);
|
||||
return initializer;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct PlatformModuleAutoRegister {
|
||||
PlatformModuleAutoRegister() {
|
||||
register_platform_module();
|
||||
}
|
||||
};
|
||||
|
||||
static PlatformModuleAutoRegister s_autoRegister;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,258 +1,92 @@
|
|||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/config/module_registry.h>
|
||||
#include <extra2d/platform/platform_module.h>
|
||||
#include <extra2d/platform/backend_factory.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <SDL.h>
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#include <switch.h>
|
||||
#endif
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
static ModuleId s_windowModuleId = INVALID_MODULE_ID;
|
||||
// 前向声明 SDL2 后端初始化函数
|
||||
void initSDL2Backend();
|
||||
|
||||
ModuleId get_window_module_id() {
|
||||
return s_windowModuleId;
|
||||
}
|
||||
WindowModule::WindowModule(const Cfg& cfg) : cfg_(cfg) {}
|
||||
|
||||
void WindowModuleConfig::applyPlatformConstraints(PlatformType platform) {
|
||||
#ifdef __SWITCH__
|
||||
(void)platform;
|
||||
windowConfig.mode = WindowMode::Fullscreen;
|
||||
windowConfig.resizable = false;
|
||||
windowConfig.highDPI = false;
|
||||
windowConfig.width = 1920;
|
||||
windowConfig.height = 1080;
|
||||
#else
|
||||
(void)platform;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool WindowModuleConfig::loadFromJson(const void* jsonData) {
|
||||
if (!jsonData) return false;
|
||||
|
||||
try {
|
||||
const json& j = *static_cast<const json*>(jsonData);
|
||||
|
||||
if (j.contains("title")) {
|
||||
windowConfig.title = j["title"].get<std::string>();
|
||||
}
|
||||
if (j.contains("width")) {
|
||||
windowConfig.width = j["width"].get<int>();
|
||||
}
|
||||
if (j.contains("height")) {
|
||||
windowConfig.height = j["height"].get<int>();
|
||||
}
|
||||
if (j.contains("minWidth")) {
|
||||
windowConfig.minWidth = j["minWidth"].get<int>();
|
||||
}
|
||||
if (j.contains("minHeight")) {
|
||||
windowConfig.minHeight = j["minHeight"].get<int>();
|
||||
}
|
||||
if (j.contains("fullscreen")) {
|
||||
windowConfig.mode = j["fullscreen"].get<bool>() ? WindowMode::Fullscreen : WindowMode::Windowed;
|
||||
}
|
||||
if (j.contains("mode")) {
|
||||
std::string modeStr = j["mode"].get<std::string>();
|
||||
if (modeStr == "fullscreen") {
|
||||
windowConfig.mode = WindowMode::Fullscreen;
|
||||
} else if (modeStr == "borderless") {
|
||||
windowConfig.mode = WindowMode::Borderless;
|
||||
} else {
|
||||
windowConfig.mode = WindowMode::Windowed;
|
||||
}
|
||||
}
|
||||
if (j.contains("vsync")) {
|
||||
windowConfig.vsync = j["vsync"].get<bool>();
|
||||
}
|
||||
if (j.contains("resizable")) {
|
||||
windowConfig.resizable = j["resizable"].get<bool>();
|
||||
}
|
||||
if (j.contains("highDPI")) {
|
||||
windowConfig.highDPI = j["highDPI"].get<bool>();
|
||||
}
|
||||
if (j.contains("multisamples")) {
|
||||
windowConfig.multisamples = j["multisamples"].get<int>();
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowModuleConfig::saveToJson(void* jsonData) const {
|
||||
if (!jsonData) return false;
|
||||
|
||||
try {
|
||||
json& j = *static_cast<json*>(jsonData);
|
||||
j["title"] = windowConfig.title;
|
||||
j["width"] = windowConfig.width;
|
||||
j["height"] = windowConfig.height;
|
||||
j["minWidth"] = windowConfig.minWidth;
|
||||
j["minHeight"] = windowConfig.minHeight;
|
||||
|
||||
switch (windowConfig.mode) {
|
||||
case WindowMode::Fullscreen:
|
||||
j["mode"] = "fullscreen";
|
||||
break;
|
||||
case WindowMode::Borderless:
|
||||
j["mode"] = "borderless";
|
||||
break;
|
||||
default:
|
||||
j["mode"] = "windowed";
|
||||
break;
|
||||
}
|
||||
|
||||
j["vsync"] = windowConfig.vsync;
|
||||
j["resizable"] = windowConfig.resizable;
|
||||
j["highDPI"] = windowConfig.highDPI;
|
||||
j["multisamples"] = windowConfig.multisamples;
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
WindowModuleInitializer::WindowModuleInitializer()
|
||||
: moduleId_(INVALID_MODULE_ID)
|
||||
, initialized_(false)
|
||||
, sdl2Initialized_(false) {
|
||||
}
|
||||
|
||||
WindowModuleInitializer::~WindowModuleInitializer() {
|
||||
WindowModule::~WindowModule() {
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowModuleInitializer::initSDL2() {
|
||||
Uint32 initFlags = SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER;
|
||||
|
||||
#ifdef __SWITCH__
|
||||
initFlags |= SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER;
|
||||
#endif
|
||||
|
||||
if (SDL_Init(initFlags) != 0) {
|
||||
E2D_LOG_ERROR("Failed to initialize SDL2: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
sdl2Initialized_ = true;
|
||||
E2D_LOG_INFO("SDL2 initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowModuleInitializer::shutdownSDL2() {
|
||||
if (!sdl2Initialized_) return;
|
||||
|
||||
SDL_Quit();
|
||||
sdl2Initialized_ = false;
|
||||
E2D_LOG_INFO("SDL2 shutdown");
|
||||
}
|
||||
|
||||
bool WindowModuleInitializer::initialize(const IModuleConfig* config) {
|
||||
bool WindowModule::init() {
|
||||
if (initialized_) return true;
|
||||
|
||||
const WindowModuleConfig* windowConfig = dynamic_cast<const WindowModuleConfig*>(config);
|
||||
if (!windowConfig) {
|
||||
E2D_LOG_ERROR("Invalid window module config");
|
||||
return false;
|
||||
}
|
||||
|
||||
windowConfig_ = windowConfig->windowConfig;
|
||||
|
||||
#ifdef __SWITCH__
|
||||
windowConfig_.mode = WindowMode::Fullscreen;
|
||||
windowConfig_.resizable = false;
|
||||
windowConfig_.highDPI = false;
|
||||
E2D_LOG_INFO("Switch platform: forcing fullscreen mode");
|
||||
cfg_.mode = WindowMode::Fullscreen;
|
||||
#endif
|
||||
|
||||
if (!initSDL2()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
extern void initSDL2Backend();
|
||||
// 初始化SDL后端(注册到工厂)
|
||||
initSDL2Backend();
|
||||
|
||||
if (!BackendFactory::has("sdl2")) {
|
||||
E2D_LOG_ERROR("SDL2 backend not registered!");
|
||||
shutdownSDL2();
|
||||
// 初始化SDL
|
||||
Uint32 flags = SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER;
|
||||
#ifdef __SWITCH__
|
||||
flags |= SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER;
|
||||
#endif
|
||||
|
||||
if (SDL_Init(flags) != 0) {
|
||||
E2D_LOG_ERROR("SDL_Init failed: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
sdlInited_ = true;
|
||||
E2D_LOG_INFO("SDL initialized successfully");
|
||||
|
||||
// 创建窗口配置
|
||||
WindowConfigData winCfg;
|
||||
winCfg.title = cfg_.title;
|
||||
winCfg.width = cfg_.w;
|
||||
winCfg.height = cfg_.h;
|
||||
winCfg.mode = cfg_.mode;
|
||||
winCfg.vsync = cfg_.vsync;
|
||||
|
||||
E2D_LOG_INFO("Creating window with size {}x{}", cfg_.w, cfg_.h);
|
||||
|
||||
// 创建窗口(使用配置的后端)
|
||||
win_ = BackendFactory::createWindow(cfg_.backend);
|
||||
if (!win_) {
|
||||
E2D_LOG_ERROR("Failed to create window backend");
|
||||
shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!createWindow(windowConfig_)) {
|
||||
if (!win_->create(winCfg)) {
|
||||
E2D_LOG_ERROR("Failed to create window");
|
||||
shutdownSDL2();
|
||||
shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("Window created successfully");
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("Window module initialized");
|
||||
E2D_LOG_INFO(" Window: {}x{}", window_->width(), window_->height());
|
||||
E2D_LOG_INFO(" Backend: SDL2");
|
||||
E2D_LOG_INFO(" VSync: {}", windowConfig_.vsync);
|
||||
E2D_LOG_INFO(" Fullscreen: {}", windowConfig_.isFullscreen());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowModuleInitializer::createWindow(const WindowConfigData& config) {
|
||||
window_ = BackendFactory::createWindow("sdl2");
|
||||
if (!window_) {
|
||||
E2D_LOG_ERROR("Failed to create SDL2 window");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!window_->create(config)) {
|
||||
E2D_LOG_ERROR("Failed to create window with specified config");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowModuleInitializer::shutdown() {
|
||||
void WindowModule::shutdown() {
|
||||
if (!initialized_) return;
|
||||
|
||||
E2D_LOG_INFO("Window module shutting down");
|
||||
|
||||
if (window_) {
|
||||
window_->destroy();
|
||||
window_.reset();
|
||||
if (win_) {
|
||||
win_->destroy();
|
||||
win_.reset();
|
||||
}
|
||||
|
||||
shutdownSDL2();
|
||||
if (sdlInited_) {
|
||||
SDL_Quit();
|
||||
sdlInited_ = false;
|
||||
}
|
||||
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
void register_window_module() {
|
||||
if (s_windowModuleId != INVALID_MODULE_ID) return;
|
||||
|
||||
s_windowModuleId = ModuleRegistry::instance().registerModule(
|
||||
makeUnique<WindowModuleConfig>(),
|
||||
[]() -> UniquePtr<IModuleInitializer> {
|
||||
auto initializer = makeUnique<WindowModuleInitializer>();
|
||||
initializer->setModuleId(s_windowModuleId);
|
||||
return initializer;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct WindowModuleAutoRegister {
|
||||
WindowModuleAutoRegister() {
|
||||
register_window_module();
|
||||
}
|
||||
};
|
||||
|
||||
static WindowModuleAutoRegister s_autoRegister;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
#include <extra2d/resource/resource_config.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
void ResourceConfigData::addSearchPath(const std::string& path) {
|
||||
if (!hasSearchPath(path)) {
|
||||
searchPaths.push_back(path);
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceConfigData::removeSearchPath(const std::string& path) {
|
||||
auto it = std::find(searchPaths.begin(), searchPaths.end(), path);
|
||||
if (it != searchPaths.end()) {
|
||||
searchPaths.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
bool ResourceConfigData::hasSearchPath(const std::string& path) const {
|
||||
return std::find(searchPaths.begin(), searchPaths.end(), path) != searchPaths.end();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <extra2d/graphics/render_command.h>
|
||||
#include <extra2d/graphics/core/render_command.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <extra2d/scene/scene.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/render_command.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/graphics/core/render_command.h>
|
||||
#include <extra2d/scene/scene.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include <algorithm>
|
||||
#include <extra2d/app/application.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/render_command.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/graphics/core/render_command.h>
|
||||
#include <extra2d/platform/iinput.h>
|
||||
#include <extra2d/scene/scene_manager.h>
|
||||
#include <extra2d/scene/transition_box_scene.h>
|
||||
|
|
@ -545,7 +545,8 @@ bool SceneManager::hasScene(const std::string &name) const {
|
|||
* 更新当前场景并分发指针事件
|
||||
*/
|
||||
void SceneManager::update(float dt) {
|
||||
if (isTransitioning_) {
|
||||
if (isTransitioning_ && activeTransitionScene_) {
|
||||
activeTransitionScene_->updateTransition(dt);
|
||||
hoverTarget_ = nullptr;
|
||||
captureTarget_ = nullptr;
|
||||
hasLastPointerWorld_ = false;
|
||||
|
|
@ -746,8 +747,9 @@ void SceneManager::finishTransition() {
|
|||
* 处理鼠标悬停、移动、点击和滚轮事件
|
||||
*/
|
||||
void SceneManager::dispatchPointerEvents(Scene &scene) {
|
||||
auto &input = Application::get().input();
|
||||
Vec2 screenPos = input.mouse();
|
||||
auto *input = Application::get().input();
|
||||
if (!input) return;
|
||||
Vec2 screenPos = input->mouse();
|
||||
|
||||
Vec2 worldPos = screenPos;
|
||||
if (auto *camera = scene.getActiveCamera()) {
|
||||
|
|
@ -784,13 +786,13 @@ void SceneManager::dispatchPointerEvents(Scene &scene) {
|
|||
dispatchToNode(hoverTarget_, evt);
|
||||
}
|
||||
|
||||
float scrollDelta = input.scrollDelta();
|
||||
float scrollDelta = input->scrollDelta();
|
||||
if (hoverTarget_ && scrollDelta != 0.0f) {
|
||||
Event evt = Event::createMouseScroll(Vec2(0.0f, scrollDelta), worldPos);
|
||||
dispatchToNode(hoverTarget_, evt);
|
||||
}
|
||||
|
||||
if (input.pressed(Mouse::Left)) {
|
||||
if (input->pressed(Mouse::Left)) {
|
||||
captureTarget_ = hoverTarget_;
|
||||
if (captureTarget_) {
|
||||
Event evt = Event::createMouseButtonPress(static_cast<int>(Mouse::Left),
|
||||
|
|
@ -804,7 +806,7 @@ void SceneManager::dispatchPointerEvents(Scene &scene) {
|
|||
}
|
||||
}
|
||||
|
||||
if (input.released(Mouse::Left)) {
|
||||
if (input->released(Mouse::Left)) {
|
||||
Node *target = captureTarget_ ? captureTarget_ : hoverTarget_;
|
||||
if (target) {
|
||||
Event evt = Event::createMouseButtonRelease(static_cast<int>(Mouse::Left),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/render_command.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/graphics/core/render_command.h>
|
||||
#include <extra2d/scene/shape_node.h>
|
||||
#include <limits>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/render_command.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/graphics/core/render_command.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/scene/sprite.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
#include <extra2d/scene/transition_box_scene.h>
|
||||
#include <extra2d/app/application.h>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <algorithm>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
|
|
@ -38,6 +39,14 @@ Ptr<TransitionBoxScene> TransitionBoxScene::create(float duration,
|
|||
void TransitionBoxScene::onTransitionStart() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新过渡进度
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*/
|
||||
void TransitionBoxScene::updateTransition(float dt) {
|
||||
TransitionScene::updateTransition(dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染过渡内容
|
||||
* @param renderer 渲染后端引用
|
||||
|
|
@ -46,11 +55,8 @@ void TransitionBoxScene::onTransitionStart() {
|
|||
*/
|
||||
void TransitionBoxScene::renderContent(RenderBackend &renderer) {
|
||||
auto &app = Application::get();
|
||||
float windowWidth = static_cast<float>(app.window().width());
|
||||
float windowHeight = static_cast<float>(app.window().height());
|
||||
|
||||
elapsed_ += 1.0f / 60.0f;
|
||||
progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f;
|
||||
float windowWidth = static_cast<float>(app.window()->width());
|
||||
float windowHeight = static_cast<float>(app.window()->height());
|
||||
|
||||
if (inScene_) {
|
||||
inScene_->renderContent(renderer);
|
||||
|
|
@ -78,10 +84,6 @@ void TransitionBoxScene::renderContent(RenderBackend &renderer) {
|
|||
renderer.fillRect(Rect(x * cellW, y * cellH, cellW + 1.0f, cellH + 1.0f),
|
||||
Colors::Black);
|
||||
}
|
||||
|
||||
if (progress_ >= 1.0f && !isFinished_) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include <extra2d/scene/transition_fade_scene.h>
|
||||
#include <extra2d/app/application.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <algorithm>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
|
@ -39,30 +40,29 @@ void TransitionFadeScene::onTransitionStart() {
|
|||
E2D_LOG_DEBUG("TransitionFadeScene::onTransitionStart - 启动淡入淡出过渡");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染过渡内容
|
||||
* @param renderer 渲染后端引用
|
||||
*
|
||||
* 根据进度渲染新旧场景,并绘制遮罩层
|
||||
*/
|
||||
void TransitionFadeScene::renderContent(RenderBackend &renderer) {
|
||||
auto &app = Application::get();
|
||||
float windowWidth = static_cast<float>(app.window().width());
|
||||
float windowHeight = static_cast<float>(app.window().height());
|
||||
|
||||
elapsed_ += 1.0f / 60.0f;
|
||||
progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f;
|
||||
void TransitionFadeScene::updateTransition(float dt) {
|
||||
// 调用基类更新进度
|
||||
TransitionScene::updateTransition(dt);
|
||||
|
||||
// 检查是否需要切换场景显示
|
||||
if (!hasSwitched_ && progress_ >= 0.5f) {
|
||||
hideOutShowIn();
|
||||
}
|
||||
}
|
||||
|
||||
void TransitionFadeScene::renderContent(RenderBackend &renderer) {
|
||||
auto &app = Application::get();
|
||||
float windowWidth = static_cast<float>(app.window()->width());
|
||||
float windowHeight = static_cast<float>(app.window()->height());
|
||||
|
||||
// 根据进度选择渲染哪个场景
|
||||
if (progress_ < 0.5f) {
|
||||
drawOutScene(renderer);
|
||||
} else {
|
||||
drawInScene(renderer);
|
||||
}
|
||||
|
||||
// 计算遮罩透明度
|
||||
float maskAlpha;
|
||||
if (progress_ < 0.5f) {
|
||||
maskAlpha = progress_ * 2.0f;
|
||||
|
|
@ -80,10 +80,6 @@ void TransitionFadeScene::renderContent(RenderBackend &renderer) {
|
|||
Color maskColor = maskColor_;
|
||||
maskColor.a = maskAlpha;
|
||||
renderer.fillRect(Rect(0.0f, 0.0f, windowWidth, windowHeight), maskColor);
|
||||
|
||||
if (progress_ >= 1.0f && !isFinished_) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include <extra2d/scene/transition_flip_scene.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/graphics/camera.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/camera/camera.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -36,6 +36,14 @@ Ptr<TransitionFlipScene> TransitionFlipScene::create(float duration,
|
|||
void TransitionFlipScene::onTransitionStart() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新过渡进度
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*/
|
||||
void TransitionFlipScene::updateTransition(float dt) {
|
||||
TransitionScene::updateTransition(dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染过渡内容
|
||||
* @param renderer 渲染后端引用
|
||||
|
|
@ -43,9 +51,6 @@ void TransitionFlipScene::onTransitionStart() {
|
|||
* 根据进度控制新旧场景的翻转角度
|
||||
*/
|
||||
void TransitionFlipScene::renderContent(RenderBackend &renderer) {
|
||||
elapsed_ += 1.0f / 60.0f;
|
||||
progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f;
|
||||
|
||||
float easeProgress = progress_ < 0.5f ? 2.0f * progress_ * progress_
|
||||
: -1.0f + (4.0f - 2.0f * progress_) * progress_;
|
||||
|
||||
|
|
@ -99,9 +104,6 @@ void TransitionFlipScene::renderContent(RenderBackend &renderer) {
|
|||
}
|
||||
}
|
||||
|
||||
if (progress_ >= 1.0f && !isFinished_) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#include <extra2d/scene/transition_scale_scene.h>
|
||||
#include <extra2d/graphics/camera.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/camera/camera.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -32,6 +32,14 @@ Ptr<TransitionScaleScene> TransitionScaleScene::create(float duration,
|
|||
void TransitionScaleScene::onTransitionStart() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新过渡进度
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*/
|
||||
void TransitionScaleScene::updateTransition(float dt) {
|
||||
TransitionScene::updateTransition(dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染过渡内容
|
||||
* @param renderer 渲染后端引用
|
||||
|
|
@ -39,9 +47,6 @@ void TransitionScaleScene::onTransitionStart() {
|
|||
* 根据进度控制新旧场景的缩放比例
|
||||
*/
|
||||
void TransitionScaleScene::renderContent(RenderBackend &renderer) {
|
||||
elapsed_ += 1.0f / 60.0f;
|
||||
progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f;
|
||||
|
||||
float easeProgress = progress_ < 0.5f ? 2.0f * progress_ * progress_
|
||||
: -1.0f + (4.0f - 2.0f * progress_) * progress_;
|
||||
|
||||
|
|
@ -79,9 +84,6 @@ void TransitionScaleScene::renderContent(RenderBackend &renderer) {
|
|||
}
|
||||
}
|
||||
|
||||
if (progress_ >= 1.0f && !isFinished_) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <extra2d/scene/transition_scene.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -50,13 +50,32 @@ void TransitionScene::onExit() {
|
|||
Scene::onExit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新过渡进度(基类默认实现)
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*
|
||||
* 子类应重写此方法更新动画进度,默认实现简单计时
|
||||
*/
|
||||
void TransitionScene::updateTransition(float dt) {
|
||||
if (isFinished_ || isCancelled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
elapsed_ += dt;
|
||||
progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f;
|
||||
|
||||
if (progress_ >= 1.0f) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 完成过渡
|
||||
*
|
||||
* 标记过渡完成并调用完成回调
|
||||
*/
|
||||
void TransitionScene::finish() {
|
||||
if (isFinished_) {
|
||||
if (isFinished_ || isCancelled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -69,6 +88,30 @@ void TransitionScene::finish() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 取消过渡
|
||||
* @param immediate 是否立即完成(false则回滚到原场景)
|
||||
*/
|
||||
void TransitionScene::cancel(bool immediate) {
|
||||
if (isFinished_ || isCancelled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
isCancelled_ = true;
|
||||
|
||||
if (immediate) {
|
||||
// 立即完成,切换到新场景
|
||||
E2D_LOG_DEBUG("TransitionScene::cancel - 立即完成过渡");
|
||||
finish();
|
||||
} else {
|
||||
// 回滚到原场景
|
||||
E2D_LOG_DEBUG("TransitionScene::cancel - 取消过渡,回滚到原场景");
|
||||
if (cancelCallback_) {
|
||||
cancelCallback_();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染过渡内容
|
||||
* @param renderer 渲染后端引用
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue