refactor(engine): 重构模块系统与平台后端
- 移除PlatformModule和LoggerModule,改为使用E2D_MODULE宏自动注册模块 - 新增ModuleRegistry和ModuleMeta系统实现模块自发现 - 将BackendFactory从PlatformModule移至独立文件 - 添加export.h统一管理导出宏 - 更新README.md添加模块自发现流程图 - 修复SDL2Input初始化状态管理问题 - 清理不再使用的平台配置相关代码 - 示例项目改为静态链接确保模块自动注册 - 添加属性绑定系统支持运行时反射
This commit is contained in:
parent
a78e6f7a05
commit
8fc3b794d2
|
|
@ -1,14 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <extra2d/core/types.h>
|
|
||||||
#include <extra2d/core/module.h>
|
|
||||||
#include <extra2d/core/service_locator.h>
|
|
||||||
#include <extra2d/config/app_config.h>
|
#include <extra2d/config/app_config.h>
|
||||||
#include <extra2d/config/config_manager.h>
|
#include <extra2d/config/config_manager.h>
|
||||||
|
#include <extra2d/core/export.h>
|
||||||
|
#include <extra2d/core/module.h>
|
||||||
|
#include <extra2d/core/module_meta.h>
|
||||||
|
#include <extra2d/core/service_locator.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
#include <extra2d/platform/iwindow.h>
|
#include <extra2d/platform/iwindow.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
#include <initializer_list>
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -17,30 +17,20 @@ class RenderBackend;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 应用程序类
|
* @brief 应用程序类
|
||||||
* 使用服务定位器模式管理模块依赖,支持依赖注入和测试Mock
|
*
|
||||||
|
* 模块现在通过 E2D_MODULE 宏自动注册,无需手动调用 use()
|
||||||
|
* Application 只负责协调初始化和生命周期管理
|
||||||
*/
|
*/
|
||||||
class Application {
|
class E2D_API Application {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief 获取单例实例
|
* @brief 获取单例实例
|
||||||
* @return 应用程序实例引用
|
* @return 应用程序实例引用
|
||||||
*/
|
*/
|
||||||
static Application& get();
|
static Application &get();
|
||||||
|
|
||||||
Application(const Application&) = delete;
|
Application(const Application &) = delete;
|
||||||
Application& operator=(const Application&) = delete;
|
Application &operator=(const Application &) = delete;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 添加模块
|
|
||||||
* @param m 模块引用
|
|
||||||
*/
|
|
||||||
void use(Module& m);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 批量添加模块
|
|
||||||
* @param modules 模块指针列表
|
|
||||||
*/
|
|
||||||
void use(std::initializer_list<Module*> modules);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 使用默认配置初始化
|
* @brief 使用默认配置初始化
|
||||||
|
|
@ -53,14 +43,14 @@ public:
|
||||||
* @param config 应用配置
|
* @param config 应用配置
|
||||||
* @return 初始化成功返回 true
|
* @return 初始化成功返回 true
|
||||||
*/
|
*/
|
||||||
bool init(const AppConfig& config);
|
bool init(const AppConfig &config);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 使用配置文件初始化
|
* @brief 使用配置文件初始化
|
||||||
* @param configPath 配置文件路径
|
* @param configPath 配置文件路径
|
||||||
* @return 初始化成功返回 true
|
* @return 初始化成功返回 true
|
||||||
*/
|
*/
|
||||||
bool init(const std::string& configPath);
|
bool init(const std::string &configPath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 关闭应用程序
|
* @brief 关闭应用程序
|
||||||
|
|
@ -103,19 +93,19 @@ public:
|
||||||
* @brief 获取窗口
|
* @brief 获取窗口
|
||||||
* @return 窗口引用
|
* @return 窗口引用
|
||||||
*/
|
*/
|
||||||
IWindow& window() { return *window_; }
|
IWindow &window() { return *window_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取渲染器
|
* @brief 获取渲染器
|
||||||
* @return 渲染器引用
|
* @return 渲染器引用
|
||||||
*/
|
*/
|
||||||
RenderBackend& renderer();
|
RenderBackend &renderer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取输入接口
|
* @brief 获取输入接口
|
||||||
* @return 输入接口引用
|
* @return 输入接口引用
|
||||||
*/
|
*/
|
||||||
IInput& input();
|
IInput &input();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取场景服务
|
* @brief 获取场景服务
|
||||||
|
|
@ -169,21 +159,38 @@ public:
|
||||||
* @brief 获取配置管理器
|
* @brief 获取配置管理器
|
||||||
* @return 配置管理器引用
|
* @return 配置管理器引用
|
||||||
*/
|
*/
|
||||||
ConfigManager& config();
|
ConfigManager &config();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取应用配置
|
* @brief 获取应用配置
|
||||||
* @return 应用配置常量引用
|
* @return 应用配置常量引用
|
||||||
*/
|
*/
|
||||||
const AppConfig& getConfig() const;
|
const AppConfig &getConfig() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取模块实例(按类型)
|
||||||
|
* @tparam T 模块类型
|
||||||
|
* @return 模块指针,不存在返回 nullptr
|
||||||
|
*/
|
||||||
|
template <typename T> T *getModule() {
|
||||||
|
return ModuleRegistry::instance().getModule<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取模块实例(按名称)
|
||||||
|
* @param name 模块名称
|
||||||
|
* @return 模块指针,不存在返回 nullptr
|
||||||
|
*/
|
||||||
|
Module *getModule(const char *name) {
|
||||||
|
return ModuleRegistry::instance().getModule(name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 注册自定义服务
|
* @brief 注册自定义服务
|
||||||
* @tparam T 服务接口类型
|
* @tparam T 服务接口类型
|
||||||
* @param service 服务实例
|
* @param service 服务实例
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template <typename T> void registerService(SharedPtr<T> service) {
|
||||||
void registerService(SharedPtr<T> service) {
|
|
||||||
ServiceLocator::instance().registerService(service);
|
ServiceLocator::instance().registerService(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,8 +199,7 @@ public:
|
||||||
* @tparam T 服务接口类型
|
* @tparam T 服务接口类型
|
||||||
* @return 服务共享指针
|
* @return 服务共享指针
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template <typename T> SharedPtr<T> getService() {
|
||||||
SharedPtr<T> getService() {
|
|
||||||
return ServiceLocator::instance().getService<T>();
|
return ServiceLocator::instance().getService<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,25 +208,17 @@ private:
|
||||||
~Application();
|
~Application();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 初始化核心模块
|
* @brief 初始化模块(从注册器创建并初始化)
|
||||||
|
* @param config 应用配置
|
||||||
* @return 初始化成功返回 true
|
* @return 初始化成功返回 true
|
||||||
*/
|
*/
|
||||||
bool initCoreModules();
|
bool initModules(const AppConfig &config);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 设置所有模块
|
|
||||||
*/
|
|
||||||
void setupAllModules();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 销毁所有模块
|
|
||||||
*/
|
|
||||||
void destroyAllModules();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 注册核心服务
|
* @brief 注册核心服务
|
||||||
*/
|
*/
|
||||||
void registerCoreServices();
|
void registerCoreServices();
|
||||||
|
void registerCameraService();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 主循环
|
* @brief 主循环
|
||||||
|
|
@ -237,8 +235,7 @@ private:
|
||||||
*/
|
*/
|
||||||
void render();
|
void render();
|
||||||
|
|
||||||
std::vector<Module*> modules_;
|
IWindow *window_ = nullptr;
|
||||||
IWindow* window_ = nullptr;
|
|
||||||
|
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
bool running_ = false;
|
bool running_ = false;
|
||||||
|
|
@ -253,4 +250,4 @@ private:
|
||||||
int currentFps_ = 0;
|
int currentFps_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // 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,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <extra2d/config/platform_config.h>
|
#include <extra2d/core/export.h>
|
||||||
#include <extra2d/core/types.h>
|
#include <extra2d/core/types.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
|
@ -21,12 +21,11 @@ namespace extra2d {
|
||||||
* @brief 应用配置结构体
|
* @brief 应用配置结构体
|
||||||
* 仅包含应用级别的配置项,模块配置由各模块自行管理
|
* 仅包含应用级别的配置项,模块配置由各模块自行管理
|
||||||
*/
|
*/
|
||||||
struct AppConfig {
|
struct E2D_API AppConfig {
|
||||||
std::string appName = "Extra2D App";
|
std::string appName = "Extra2D App";
|
||||||
std::string appVersion = "1.0.0";
|
std::string appVersion = "1.0.0";
|
||||||
std::string organization = "";
|
std::string organization = "";
|
||||||
std::string configFile = "config.json";
|
std::string configFile = "config.json";
|
||||||
PlatformType targetPlatform = PlatformType::Auto;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建默认配置
|
* @brief 创建默认配置
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include <extra2d/config/app_config.h>
|
#include <extra2d/config/app_config.h>
|
||||||
#include <extra2d/config/config_loader.h>
|
#include <extra2d/config/config_loader.h>
|
||||||
#include <extra2d/config/platform_config.h>
|
|
||||||
#include <extra2d/core/types.h>
|
#include <extra2d/core/types.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -114,18 +113,6 @@ public:
|
||||||
*/
|
*/
|
||||||
void setAppConfig(const AppConfig &config);
|
void setAppConfig(const AppConfig &config);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台配置
|
|
||||||
* @return 平台配置接口指针
|
|
||||||
*/
|
|
||||||
PlatformConfig *platformConfig();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台配置(常量版本)
|
|
||||||
* @return 平台配置接口常量指针
|
|
||||||
*/
|
|
||||||
const PlatformConfig *platformConfig() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 注册配置变更回调
|
* @brief 注册配置变更回调
|
||||||
* @param callback 回调函数
|
* @param callback 回调函数
|
||||||
|
|
@ -273,7 +260,6 @@ private:
|
||||||
void notifyChangeCallbacks(const ConfigChangeEvent &event);
|
void notifyChangeCallbacks(const ConfigChangeEvent &event);
|
||||||
|
|
||||||
AppConfig m_appConfig;
|
AppConfig m_appConfig;
|
||||||
UniquePtr<PlatformConfig> m_platformConfig;
|
|
||||||
UniquePtr<ConfigLoader> m_loader;
|
UniquePtr<ConfigLoader> m_loader;
|
||||||
std::string m_configPath;
|
std::string m_configPath;
|
||||||
bool m_initialized = false;
|
bool m_initialized = false;
|
||||||
|
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <extra2d/core/types.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file platform_config.h
|
|
||||||
* @brief 平台配置接口
|
|
||||||
*
|
|
||||||
* 平台配置只提供平台能力信息,不再直接修改应用配置。
|
|
||||||
* 各模块通过 IModuleConfig::applyPlatformConstraints() 处理平台约束。
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 平台类型枚举
|
|
||||||
*/
|
|
||||||
enum class PlatformType {
|
|
||||||
Auto,
|
|
||||||
Windows,
|
|
||||||
Switch,
|
|
||||||
Linux,
|
|
||||||
macOS
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 平台能力结构
|
|
||||||
*/
|
|
||||||
struct PlatformCapabilities {
|
|
||||||
bool supportsWindowed = true;
|
|
||||||
bool supportsFullscreen = true;
|
|
||||||
bool supportsBorderless = true;
|
|
||||||
bool supportsCursor = true;
|
|
||||||
bool supportsCursorHide = true;
|
|
||||||
bool supportsDPIAwareness = true;
|
|
||||||
bool supportsVSync = true;
|
|
||||||
bool supportsMultiMonitor = true;
|
|
||||||
bool supportsClipboard = true;
|
|
||||||
bool supportsGamepad = true;
|
|
||||||
bool supportsTouch = false;
|
|
||||||
bool supportsKeyboard = true;
|
|
||||||
bool supportsMouse = true;
|
|
||||||
bool supportsResize = true;
|
|
||||||
bool supportsHighDPI = true;
|
|
||||||
int maxTextureSize = 16384;
|
|
||||||
int preferredScreenWidth = 1920;
|
|
||||||
int preferredScreenHeight = 1080;
|
|
||||||
float defaultDPI = 96.0f;
|
|
||||||
|
|
||||||
bool hasWindowSupport() const { return supportsWindowed || supportsFullscreen || supportsBorderless; }
|
|
||||||
bool hasInputSupport() const { return supportsKeyboard || supportsMouse || supportsGamepad || supportsTouch; }
|
|
||||||
bool isDesktop() const { return supportsKeyboard && supportsMouse && supportsWindowed; }
|
|
||||||
bool isConsole() const { return !supportsWindowed && supportsGamepad; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 平台配置抽象接口
|
|
||||||
*/
|
|
||||||
class PlatformConfig {
|
|
||||||
public:
|
|
||||||
virtual ~PlatformConfig() = default;
|
|
||||||
|
|
||||||
virtual PlatformType platformType() const = 0;
|
|
||||||
virtual const char* platformName() const = 0;
|
|
||||||
virtual const PlatformCapabilities& capabilities() const = 0;
|
|
||||||
|
|
||||||
virtual int getRecommendedWidth() const = 0;
|
|
||||||
virtual int getRecommendedHeight() const = 0;
|
|
||||||
virtual bool isResolutionSupported(int width, int height) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建平台配置实例
|
|
||||||
* @param type 平台类型,默认为 Auto(自动检测)
|
|
||||||
* @return 平台配置的智能指针
|
|
||||||
*/
|
|
||||||
UniquePtr<PlatformConfig> createPlatformConfig(PlatformType type = PlatformType::Auto);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台类型名称
|
|
||||||
* @param type 平台类型枚举值
|
|
||||||
* @return 平台名称字符串
|
|
||||||
*/
|
|
||||||
const char* getPlatformTypeName(PlatformType type);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,210 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <extra2d/config/app_config.h>
|
|
||||||
#include <extra2d/config/platform_config.h>
|
|
||||||
#include <extra2d/core/types.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// 平台检测器工具类
|
|
||||||
// ============================================================================
|
|
||||||
class PlatformDetector {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief 检测当前运行平台
|
|
||||||
* @return 当前平台的类型
|
|
||||||
*/
|
|
||||||
static PlatformType detect();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台名称字符串
|
|
||||||
* @return 平台名称(如 "Windows", "Linux", "macOS", "Switch")
|
|
||||||
*/
|
|
||||||
static const char* platformName();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取指定平台类型的名称
|
|
||||||
* @param type 平台类型
|
|
||||||
* @return 平台名称字符串
|
|
||||||
*/
|
|
||||||
static const char* platformName(PlatformType type);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查当前平台是否为桌面平台
|
|
||||||
* @return 如果是桌面平台返回 true
|
|
||||||
*/
|
|
||||||
static bool isDesktopPlatform();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查当前平台是否为游戏主机平台
|
|
||||||
* @return 如果是游戏主机平台返回 true
|
|
||||||
*/
|
|
||||||
static bool isConsolePlatform();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查当前平台是否为移动平台
|
|
||||||
* @return 如果是移动平台返回 true
|
|
||||||
*/
|
|
||||||
static bool isMobilePlatform();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取当前平台的能力
|
|
||||||
* @return 平台能力结构
|
|
||||||
*/
|
|
||||||
static PlatformCapabilities capabilities();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取指定平台的能力
|
|
||||||
* @param type 平台类型
|
|
||||||
* @return 平台能力结构
|
|
||||||
*/
|
|
||||||
static PlatformCapabilities capabilities(PlatformType type);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取当前平台的默认配置
|
|
||||||
* @return 平台默认的应用配置
|
|
||||||
*/
|
|
||||||
static AppConfig platformDefaults();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取指定平台的默认配置
|
|
||||||
* @param type 平台类型
|
|
||||||
* @return 平台默认的应用配置
|
|
||||||
*/
|
|
||||||
static AppConfig platformDefaults(PlatformType type);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取当前平台的推荐分辨率
|
|
||||||
* @param width 输出宽度
|
|
||||||
* @param height 输出高度
|
|
||||||
*/
|
|
||||||
static void getRecommendedResolution(int& width, int& height);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取当前平台的默认 DPI
|
|
||||||
* @return 默认 DPI 值
|
|
||||||
*/
|
|
||||||
static float getDefaultDPI();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查当前平台是否支持指定功能
|
|
||||||
* @param feature 功能名称
|
|
||||||
* @return 如果支持返回 true
|
|
||||||
*/
|
|
||||||
static bool supportsFeature(const std::string& feature);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取系统内存大小
|
|
||||||
* @return 系统内存大小(MB),如果无法获取返回 0
|
|
||||||
*/
|
|
||||||
static int getSystemMemoryMB();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取 CPU 核心数
|
|
||||||
* @return CPU 核心数
|
|
||||||
*/
|
|
||||||
static int getCPUCoreCount();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查是否支持多线程渲染
|
|
||||||
* @return 如果支持返回 true
|
|
||||||
*/
|
|
||||||
static bool supportsMultithreadedRendering();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台特定的配置路径
|
|
||||||
* @param appName 应用名称
|
|
||||||
* @return 配置文件目录路径
|
|
||||||
*/
|
|
||||||
static std::string getConfigPath(const std::string& appName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台特定的存档路径
|
|
||||||
* @param appName 应用名称
|
|
||||||
* @return 存档文件目录路径
|
|
||||||
*/
|
|
||||||
static std::string getSavePath(const std::string& appName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台特定的缓存路径
|
|
||||||
* @param appName 应用名称
|
|
||||||
* @return 缓存文件目录路径
|
|
||||||
*/
|
|
||||||
static std::string getCachePath(const std::string& appName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台特定的日志路径
|
|
||||||
* @param appName 应用名称
|
|
||||||
* @return 日志文件目录路径
|
|
||||||
*/
|
|
||||||
static std::string getLogPath(const std::string& appName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台特定的资源路径(Shader、纹理等)
|
|
||||||
* Switch平台使用romfs,其他平台使用相对路径
|
|
||||||
* @param appName 应用名称
|
|
||||||
* @return 资源目录路径
|
|
||||||
*/
|
|
||||||
static std::string getResourcePath(const std::string& appName = "");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台特定的Shader路径
|
|
||||||
* @param appName 应用名称
|
|
||||||
* @return Shader目录路径
|
|
||||||
*/
|
|
||||||
static std::string getShaderPath(const std::string& appName = "");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台特定的Shader缓存路径
|
|
||||||
* Switch平台使用sdmc,其他平台使用系统缓存目录
|
|
||||||
* @param appName 应用名称
|
|
||||||
* @return Shader缓存目录路径
|
|
||||||
*/
|
|
||||||
static std::string getShaderCachePath(const std::string& appName = "");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查平台是否使用romfs(只读文件系统)
|
|
||||||
* @return 使用romfs返回true
|
|
||||||
*/
|
|
||||||
static bool usesRomfs();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查平台是否支持热重载
|
|
||||||
* Switch平台不支持热重载(romfs只读)
|
|
||||||
* @return 支持热重载返回true
|
|
||||||
*/
|
|
||||||
static bool supportsHotReload();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查平台是否为小端字节序
|
|
||||||
* @return 如果是小端字节序返回 true
|
|
||||||
*/
|
|
||||||
static bool isLittleEndian();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查平台是否为大端字节序
|
|
||||||
* @return 如果是大端字节序返回 true
|
|
||||||
*/
|
|
||||||
static bool isBigEndian();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台信息摘要
|
|
||||||
* @return 平台信息字符串
|
|
||||||
*/
|
|
||||||
static std::string getPlatformSummary();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static PlatformCapabilities getWindowsCapabilities();
|
|
||||||
static PlatformCapabilities getLinuxCapabilities();
|
|
||||||
static PlatformCapabilities getMacOSCapabilities();
|
|
||||||
static PlatformCapabilities getSwitchCapabilities();
|
|
||||||
|
|
||||||
static AppConfig getWindowsDefaults();
|
|
||||||
static AppConfig getLinuxDefaults();
|
|
||||||
static AppConfig getMacOSDefaults();
|
|
||||||
static AppConfig getSwitchDefaults();
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// 动态库导出宏
|
||||||
|
// 静态库时 E2D_API 为空
|
||||||
|
#ifndef E2D_BUILDING_DLL
|
||||||
|
#define E2D_API
|
||||||
|
#else
|
||||||
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||||
|
#define E2D_API __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define E2D_API __attribute__((visibility("default")))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 模板类导出(不需要导出)
|
||||||
|
#define E2D_TEMPLATE_API
|
||||||
|
|
||||||
|
// 内联函数导出
|
||||||
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||||
|
#define E2D_INLINE __forceinline
|
||||||
|
#else
|
||||||
|
#define E2D_INLINE inline
|
||||||
|
#endif
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/core/export.h>
|
||||||
#include <extra2d/core/types.h>
|
#include <extra2d/core/types.h>
|
||||||
|
#include <extra2d/core/property.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -14,7 +16,7 @@ class EventContext;
|
||||||
* @brief 模块上下文基类
|
* @brief 模块上下文基类
|
||||||
* 用于遍历模块链,支持链式调用
|
* 用于遍历模块链,支持链式调用
|
||||||
*/
|
*/
|
||||||
class ModuleContext {
|
class E2D_API ModuleContext {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief 析构函数
|
* @brief 析构函数
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/core/export.h>
|
||||||
|
#include <extra2d/core/module_meta.h>
|
||||||
|
#include <extra2d/core/property.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 模块元数据模板实现
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
class ModuleMeta : public ModuleMetaBase {
|
||||||
|
public:
|
||||||
|
using ModuleType = T;
|
||||||
|
|
||||||
|
const char* name_ = nullptr;
|
||||||
|
int priority_ = 0;
|
||||||
|
std::vector<const char*> dependencies_;
|
||||||
|
std::function<void(Module*, PropertyBinder&)> bindFunc_;
|
||||||
|
|
||||||
|
const char* getName() const override { return name_; }
|
||||||
|
int getPriority() const override { return priority_; }
|
||||||
|
std::vector<const char*> getDependencies() const override { return dependencies_; }
|
||||||
|
|
||||||
|
T* create() override {
|
||||||
|
return new T();
|
||||||
|
}
|
||||||
|
|
||||||
|
void bindProperties(Module* instance, PropertyBinder& binder) override {
|
||||||
|
if (bindFunc_) {
|
||||||
|
bindFunc_(instance, binder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 模块注册辅助类(静态自动注册)
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
struct ModuleAutoRegister {
|
||||||
|
ModuleMeta<T> meta;
|
||||||
|
|
||||||
|
ModuleAutoRegister(const char* name, int priority, std::initializer_list<const char*> deps) {
|
||||||
|
meta.name_ = name;
|
||||||
|
meta.priority_ = priority;
|
||||||
|
meta.dependencies_ = std::vector<const char*>(deps);
|
||||||
|
ModuleRegistry::instance().registerMeta(&meta);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 模块定义宏 - 静态自动注册
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 简化版模块定义(静态自动注册)
|
||||||
|
*
|
||||||
|
* 用于引擎内部模块,在动态库加载时自动注册
|
||||||
|
*/
|
||||||
|
#define E2D_MODULE(ModuleClassName, priorityValue, ...) \
|
||||||
|
__attribute__((used)) \
|
||||||
|
static ::extra2d::detail::ModuleAutoRegister< ::extra2d::ModuleClassName> \
|
||||||
|
E2D_CONCAT(_e2d_auto_reg_, ModuleClassName)( \
|
||||||
|
#ModuleClassName, priorityValue, { __VA_ARGS__ });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 外部模块定义(自动生成 force_link 函数)
|
||||||
|
*
|
||||||
|
* 用于编译为单独 DLL 的自定义模块
|
||||||
|
*
|
||||||
|
* 使用示例(在模块 cpp 文件末尾):
|
||||||
|
* } // namespace extra2d
|
||||||
|
* E2D_MODULE_EXPORT(HelloModule, 1000)
|
||||||
|
*/
|
||||||
|
#define E2D_MODULE_EXPORT(ModuleClassName, priorityValue, ...) \
|
||||||
|
E2D_MODULE(ModuleClassName, priorityValue, __VA_ARGS__) \
|
||||||
|
extern "C" E2D_API void E2D_CONCAT(e2d_force_link_, ModuleClassName)() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 声明外部模块的 force_link 函数
|
||||||
|
*/
|
||||||
|
#define E2D_DECLARE_FORCE_LINK(ModuleClassName) \
|
||||||
|
extern "C" void E2D_CONCAT(e2d_force_link_, ModuleClassName)()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 调用 force_link 函数
|
||||||
|
*/
|
||||||
|
#define E2D_CALL_FORCE_LINK(ModuleClassName) \
|
||||||
|
E2D_CONCAT(e2d_force_link_, ModuleClassName)()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 强制链接外部模块(声明 + 调用)
|
||||||
|
*
|
||||||
|
* 在 main.cpp 开头调用,触发 DLL 静态初始化
|
||||||
|
*/
|
||||||
|
#define E2D_FORCE_LINK(ModuleClassName) \
|
||||||
|
E2D_DECLARE_FORCE_LINK(ModuleClassName); \
|
||||||
|
E2D_CALL_FORCE_LINK(ModuleClassName)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 带属性的模块定义开始
|
||||||
|
*/
|
||||||
|
#define E2D_MODULE_BEGIN(ModuleClassName) \
|
||||||
|
namespace { \
|
||||||
|
static ::extra2d::ModuleMeta< ::extra2d::ModuleClassName>& E2D_CONCAT(_e2d_get_meta_, ModuleClassName)() { \
|
||||||
|
static ::extra2d::ModuleMeta< ::extra2d::ModuleClassName> meta; \
|
||||||
|
return meta; \
|
||||||
|
} \
|
||||||
|
struct E2D_CONCAT(_E2D_ModuleCfg_, ModuleClassName) { \
|
||||||
|
E2D_CONCAT(_E2D_ModuleCfg_, ModuleClassName)()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 定义模块优先级
|
||||||
|
*/
|
||||||
|
#define E2D_PRIORITY(value) \
|
||||||
|
{ auto& m = E2D_CONCAT(_e2d_get_meta_, ModuleClassName)(); m.priority_ = value; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 定义模块依赖
|
||||||
|
*/
|
||||||
|
#define E2D_DEPENDENCIES(...) \
|
||||||
|
{ \
|
||||||
|
auto& m = E2D_CONCAT(_e2d_get_meta_, ModuleClassName)(); \
|
||||||
|
m.dependencies_ = { __VA_ARGS__ }; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 定义属性
|
||||||
|
*/
|
||||||
|
#define E2D_PROPERTY(name, type) \
|
||||||
|
{ \
|
||||||
|
auto& m = E2D_CONCAT(_e2d_get_meta_, ModuleClassName)(); \
|
||||||
|
auto oldFunc = m.bindFunc_; \
|
||||||
|
m.bindFunc_ = [oldFunc](::extra2d::Module* inst, ::extra2d::PropertyBinder& binder) { \
|
||||||
|
if (oldFunc) oldFunc(inst, binder); \
|
||||||
|
auto* module = static_cast< ::extra2d::ModuleClassName*>(inst); \
|
||||||
|
binder.bind<type>(#name, module->name, #name, ""); \
|
||||||
|
}; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 结束模块定义
|
||||||
|
*/
|
||||||
|
#define E2D_MODULE_END() \
|
||||||
|
} E2D_CONCAT(_e2d_cfg_inst_, ModuleClassName); \
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,146 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/core/export.h>
|
||||||
|
#include <extra2d/core/property.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
class Module;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 模块元数据基类
|
||||||
|
*
|
||||||
|
* 提供模块的类型信息、依赖关系和创建工厂
|
||||||
|
*/
|
||||||
|
struct E2D_API ModuleMetaBase {
|
||||||
|
virtual ~ModuleMetaBase() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取模块名称
|
||||||
|
*/
|
||||||
|
virtual const char* getName() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取模块优先级
|
||||||
|
*/
|
||||||
|
virtual int getPriority() const { return 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取模块依赖
|
||||||
|
*/
|
||||||
|
virtual std::vector<const char*> getDependencies() const { return {}; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建模块实例
|
||||||
|
*/
|
||||||
|
virtual Module* create() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绑定模块属性
|
||||||
|
*/
|
||||||
|
virtual void bindProperties(Module* instance, PropertyBinder& binder) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 模块注册器(自动发现)
|
||||||
|
*
|
||||||
|
* 单例类,管理所有模块的注册、创建和生命周期
|
||||||
|
*/
|
||||||
|
class E2D_API ModuleRegistry {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 获取单例实例
|
||||||
|
*/
|
||||||
|
static ModuleRegistry& instance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 注册模块元数据
|
||||||
|
* @param meta 模块元数据指针
|
||||||
|
*/
|
||||||
|
void registerMeta(ModuleMetaBase* meta);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取所有已注册模块元数据
|
||||||
|
*/
|
||||||
|
std::vector<ModuleMetaBase*> getAllMetas() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 按名称获取模块元数据
|
||||||
|
*/
|
||||||
|
ModuleMetaBase* getMeta(const char* name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建并初始化所有模块
|
||||||
|
* @return 成功返回 true
|
||||||
|
*/
|
||||||
|
bool createAndInitAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 销毁所有模块
|
||||||
|
*/
|
||||||
|
void destroyAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取模块实例(按类型)
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
T* getModule() const {
|
||||||
|
for (const auto& [name, ptr] : instanceMap_) {
|
||||||
|
if (auto* derived = dynamic_cast<T*>(ptr)) {
|
||||||
|
return derived;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取模块实例(按名称)
|
||||||
|
*/
|
||||||
|
Module* getModule(const char* name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查模块是否存在
|
||||||
|
*/
|
||||||
|
bool hasModule(const char* name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取所有模块实例
|
||||||
|
*/
|
||||||
|
std::vector<Module*> getAllModules() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否已初始化
|
||||||
|
*/
|
||||||
|
bool isInitialized() const { return initialized_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ModuleRegistry() = default;
|
||||||
|
~ModuleRegistry() = default;
|
||||||
|
ModuleRegistry(const ModuleRegistry&) = delete;
|
||||||
|
ModuleRegistry& operator=(const ModuleRegistry&) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 按依赖关系拓扑排序
|
||||||
|
*/
|
||||||
|
std::vector<ModuleMetaBase*> sortByDependency();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检测循环依赖
|
||||||
|
*/
|
||||||
|
bool hasCircularDependency() const;
|
||||||
|
|
||||||
|
std::vector<ModuleMetaBase*> metas_;
|
||||||
|
std::vector<std::unique_ptr<Module>> instances_;
|
||||||
|
std::unordered_map<std::string, Module*> instanceMap_;
|
||||||
|
bool initialized_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,184 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/core/color.h>
|
||||||
|
#include <extra2d/core/math_types.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 属性值类型
|
||||||
|
*
|
||||||
|
* 支持多种基础类型的运行时存储和查询
|
||||||
|
*/
|
||||||
|
using PropertyValue = std::variant<
|
||||||
|
std::monostate,
|
||||||
|
bool,
|
||||||
|
int,
|
||||||
|
float,
|
||||||
|
double,
|
||||||
|
std::string,
|
||||||
|
Vec2,
|
||||||
|
Vec3,
|
||||||
|
Color,
|
||||||
|
Rect,
|
||||||
|
Size
|
||||||
|
>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 属性元数据
|
||||||
|
*
|
||||||
|
* 包含属性的描述信息,用于编辑器和序列化
|
||||||
|
*/
|
||||||
|
struct PropertyMeta {
|
||||||
|
const char* name = nullptr;
|
||||||
|
const char* displayName = nullptr;
|
||||||
|
const char* description = nullptr;
|
||||||
|
PropertyValue defaultValue{};
|
||||||
|
bool editable = true;
|
||||||
|
bool serializable = true;
|
||||||
|
|
||||||
|
PropertyMeta() = default;
|
||||||
|
|
||||||
|
PropertyMeta(const char* n, const char* dn, const char* desc,
|
||||||
|
PropertyValue def, bool edit = true, bool ser = true)
|
||||||
|
: name(n), displayName(dn), description(desc),
|
||||||
|
defaultValue(def), editable(edit), serializable(ser) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 属性访问器
|
||||||
|
*
|
||||||
|
* 用于运行时读写属性值
|
||||||
|
*/
|
||||||
|
struct PropertyAccessor {
|
||||||
|
std::function<PropertyValue()> getter;
|
||||||
|
std::function<bool(const PropertyValue&)> setter;
|
||||||
|
PropertyMeta meta;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 属性绑定器基类
|
||||||
|
*
|
||||||
|
* 提供属性的绑定、查询和修改接口
|
||||||
|
*/
|
||||||
|
class PropertyBinder {
|
||||||
|
public:
|
||||||
|
virtual ~PropertyBinder() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取所有属性元数据
|
||||||
|
* @return 属性元数据列表
|
||||||
|
*/
|
||||||
|
virtual std::vector<PropertyMeta> getProperties() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取属性值
|
||||||
|
* @param name 属性名
|
||||||
|
* @return 属性值
|
||||||
|
*/
|
||||||
|
virtual PropertyValue getProperty(const char* name) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置属性值
|
||||||
|
* @param name 属性名
|
||||||
|
* @param value 属性值
|
||||||
|
* @return 设置成功返回 true
|
||||||
|
*/
|
||||||
|
virtual bool setProperty(const char* name, const PropertyValue& value) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否有指定属性
|
||||||
|
* @param name 属性名
|
||||||
|
* @return 存在返回 true
|
||||||
|
*/
|
||||||
|
virtual bool hasProperty(const char* name) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取属性元数据
|
||||||
|
* @param name 属性名
|
||||||
|
* @return 属性元数据指针,不存在返回 nullptr
|
||||||
|
*/
|
||||||
|
virtual const PropertyMeta* getPropertyMeta(const char* name) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 属性绑定器实现
|
||||||
|
*
|
||||||
|
* 使用函数指针实现属性的运行时访问
|
||||||
|
*/
|
||||||
|
class PropertyBinderImpl : public PropertyBinder {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 绑定属性
|
||||||
|
* @tparam T 属性类型
|
||||||
|
* @param name 属性名
|
||||||
|
* @param value 属性引用
|
||||||
|
* @param displayName 显示名
|
||||||
|
* @param description 描述
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
void bind(const char* name, T& value,
|
||||||
|
const char* displayName = nullptr,
|
||||||
|
const char* description = nullptr) {
|
||||||
|
PropertyAccessor accessor;
|
||||||
|
accessor.meta = PropertyMeta(name, displayName ? displayName : name,
|
||||||
|
description ? description : "", T{});
|
||||||
|
accessor.getter = [&value]() -> PropertyValue { return value; };
|
||||||
|
accessor.setter = [&value](const PropertyValue& v) -> bool {
|
||||||
|
if (auto* ptr = std::get_if<T>(&v)) {
|
||||||
|
value = *ptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
accessors_[name] = std::move(accessor);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PropertyMeta> getProperties() const override {
|
||||||
|
std::vector<PropertyMeta> result;
|
||||||
|
result.reserve(accessors_.size());
|
||||||
|
for (const auto& [name, accessor] : accessors_) {
|
||||||
|
result.push_back(accessor.meta);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyValue getProperty(const char* name) const override {
|
||||||
|
auto it = accessors_.find(name);
|
||||||
|
if (it != accessors_.end() && it->second.getter) {
|
||||||
|
return it->second.getter();
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setProperty(const char* name, const PropertyValue& value) override {
|
||||||
|
auto it = accessors_.find(name);
|
||||||
|
if (it != accessors_.end() && it->second.setter) {
|
||||||
|
return it->second.setter(value);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasProperty(const char* name) const override {
|
||||||
|
return accessors_.find(name) != accessors_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
const PropertyMeta* getPropertyMeta(const char* name) const override {
|
||||||
|
auto it = accessors_.find(name);
|
||||||
|
if (it != accessors_.end()) {
|
||||||
|
return &it->second.meta;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, PropertyAccessor> accessors_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -6,23 +6,20 @@
|
||||||
// Core
|
// Core
|
||||||
#include <extra2d/core/color.h>
|
#include <extra2d/core/color.h>
|
||||||
#include <extra2d/core/math_types.h>
|
#include <extra2d/core/math_types.h>
|
||||||
#include <extra2d/core/types.h>
|
|
||||||
#include <extra2d/core/module.h>
|
#include <extra2d/core/module.h>
|
||||||
|
#include <extra2d/core/module_macros.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
#include <extra2d/config/app_config.h>
|
#include <extra2d/config/app_config.h>
|
||||||
#include <extra2d/config/config_loader.h>
|
#include <extra2d/config/config_loader.h>
|
||||||
#include <extra2d/config/config_manager.h>
|
#include <extra2d/config/config_manager.h>
|
||||||
#include <extra2d/config/platform_config.h>
|
|
||||||
#include <extra2d/config/platform_detector.h>
|
|
||||||
|
|
||||||
// Modules
|
// Modules
|
||||||
#include <extra2d/modules/config_module.h>
|
#include <extra2d/modules/config_module.h>
|
||||||
#include <extra2d/modules/logger_module.h>
|
|
||||||
#include <extra2d/modules/platform_module.h>
|
|
||||||
#include <extra2d/modules/window_module.h>
|
|
||||||
#include <extra2d/modules/input_module.h>
|
#include <extra2d/modules/input_module.h>
|
||||||
#include <extra2d/modules/render_module.h>
|
#include <extra2d/modules/render_module.h>
|
||||||
|
#include <extra2d/modules/window_module.h>
|
||||||
|
|
||||||
// Platform
|
// Platform
|
||||||
#include <extra2d/platform/iinput.h>
|
#include <extra2d/platform/iinput.h>
|
||||||
|
|
@ -61,10 +58,10 @@
|
||||||
#include <extra2d/utils/timer.h>
|
#include <extra2d/utils/timer.h>
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
|
#include <extra2d/services/camera_service.h>
|
||||||
#include <extra2d/services/event_service.h>
|
#include <extra2d/services/event_service.h>
|
||||||
#include <extra2d/services/scene_service.h>
|
#include <extra2d/services/scene_service.h>
|
||||||
#include <extra2d/services/timer_service.h>
|
#include <extra2d/services/timer_service.h>
|
||||||
#include <extra2d/services/camera_service.h>
|
|
||||||
|
|
||||||
// Application
|
// Application
|
||||||
#include <extra2d/app/application.h>
|
#include <extra2d/app/application.h>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <extra2d/config/platform_detector.h>
|
|
||||||
#include <extra2d/graphics/shader_cache.h>
|
#include <extra2d/graphics/shader_cache.h>
|
||||||
#include <extra2d/graphics/shader_hot_reloader.h>
|
#include <extra2d/graphics/shader_hot_reloader.h>
|
||||||
#include <extra2d/graphics/shader_interface.h>
|
#include <extra2d/graphics/shader_interface.h>
|
||||||
|
|
|
||||||
|
|
@ -67,9 +67,14 @@ public:
|
||||||
* @brief 设置窗口
|
* @brief 设置窗口
|
||||||
* @param window 窗口接口指针
|
* @param window 窗口接口指针
|
||||||
*/
|
*/
|
||||||
void setWindow(IWindow* window) { window_ = window; }
|
void setWindow(IWindow* window);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 使用窗口初始化输入
|
||||||
|
*/
|
||||||
|
void initializeWithWindow();
|
||||||
|
|
||||||
IWindow* window_ = nullptr;
|
IWindow* window_ = nullptr;
|
||||||
IInput* input_ = nullptr;
|
IInput* input_ = nullptr;
|
||||||
InputConfigData config_;
|
InputConfigData config_;
|
||||||
|
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <extra2d/core/module.h>
|
|
||||||
#include <extra2d/utils/logger.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 日志模块
|
|
||||||
* 管理日志系统的初始化和配置
|
|
||||||
*/
|
|
||||||
class LoggerModule : public Module {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief 构造函数
|
|
||||||
*/
|
|
||||||
LoggerModule();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 析构函数
|
|
||||||
*/
|
|
||||||
~LoggerModule() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取模块名称
|
|
||||||
* @return 模块名称
|
|
||||||
*/
|
|
||||||
const char* getName() const override { return "Logger"; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取模块优先级
|
|
||||||
* @return 优先级(核心模块,最先初始化)
|
|
||||||
*/
|
|
||||||
int getPriority() const override { return -1; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 设置模块
|
|
||||||
*/
|
|
||||||
void setupModule() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 销毁模块
|
|
||||||
*/
|
|
||||||
void destroyModule() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 设置日志级别
|
|
||||||
* @param level 日志级别
|
|
||||||
*/
|
|
||||||
void setLogLevel(LogLevel level) { logLevel_ = level; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 设置控制台输出
|
|
||||||
* @param enabled 是否启用
|
|
||||||
*/
|
|
||||||
void setConsoleOutput(bool enabled) { consoleOutput_ = enabled; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 设置文件输出
|
|
||||||
* @param filePath 日志文件路径
|
|
||||||
*/
|
|
||||||
void setFileOutput(const std::string& filePath) {
|
|
||||||
fileOutput_ = true;
|
|
||||||
logFilePath_ = filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
LogLevel logLevel_ = LogLevel::Info;
|
|
||||||
bool consoleOutput_ = true;
|
|
||||||
bool fileOutput_ = false;
|
|
||||||
std::string logFilePath_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <extra2d/core/module.h>
|
|
||||||
#include <extra2d/config/platform_config.h>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 平台模块
|
|
||||||
* 管理平台相关的初始化和配置
|
|
||||||
*/
|
|
||||||
class PlatformModule : public Module {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief 构造函数
|
|
||||||
*/
|
|
||||||
PlatformModule();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 析构函数
|
|
||||||
*/
|
|
||||||
~PlatformModule() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取模块名称
|
|
||||||
* @return 模块名称
|
|
||||||
*/
|
|
||||||
const char* getName() const override { return "Platform"; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取模块优先级
|
|
||||||
* @return 优先级
|
|
||||||
*/
|
|
||||||
int getPriority() const override { return 10; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 设置模块
|
|
||||||
*/
|
|
||||||
void setupModule() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 销毁模块
|
|
||||||
*/
|
|
||||||
void destroyModule() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 设置目标平台
|
|
||||||
* @param platform 目标平台类型
|
|
||||||
*/
|
|
||||||
void setTargetPlatform(PlatformType platform) { targetPlatform_ = platform; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取当前平台
|
|
||||||
* @return 当前平台类型
|
|
||||||
*/
|
|
||||||
PlatformType getPlatform() const { return resolvedPlatform_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台配置
|
|
||||||
* @return 平台配置指针
|
|
||||||
*/
|
|
||||||
PlatformConfig* getPlatformConfig() const { return platformConfig_.get(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* @brief 初始化 Switch 平台
|
|
||||||
* @return 初始化成功返回 true
|
|
||||||
*/
|
|
||||||
bool initSwitch();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 关闭 Switch 平台
|
|
||||||
*/
|
|
||||||
void shutdownSwitch();
|
|
||||||
|
|
||||||
PlatformType targetPlatform_ = PlatformType::Auto;
|
|
||||||
PlatformType resolvedPlatform_ = PlatformType::Windows;
|
|
||||||
UniquePtr<PlatformConfig> platformConfig_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -99,7 +99,7 @@ public:
|
||||||
* @brief 设置窗口
|
* @brief 设置窗口
|
||||||
* @param window 窗口接口指针
|
* @param window 窗口接口指针
|
||||||
*/
|
*/
|
||||||
void setWindow(IWindow* window) { window_ = window; }
|
void setWindow(IWindow* window);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取渲染器
|
* @brief 获取渲染器
|
||||||
|
|
@ -108,6 +108,11 @@ public:
|
||||||
RenderBackend* getRenderer() const { return renderer_.get(); }
|
RenderBackend* getRenderer() const { return renderer_.get(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 使用窗口初始化渲染器
|
||||||
|
*/
|
||||||
|
void initializeWithWindow();
|
||||||
|
|
||||||
IWindow* window_ = nullptr;
|
IWindow* window_ = nullptr;
|
||||||
UniquePtr<RenderBackend> renderer_;
|
UniquePtr<RenderBackend> renderer_;
|
||||||
RenderModuleConfig config_;
|
RenderModuleConfig config_;
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,6 @@
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 平台模块配置
|
|
||||||
*/
|
|
||||||
struct PlatformModuleConfig {
|
|
||||||
std::string backend = "sdl2";
|
|
||||||
bool gamepad = true;
|
|
||||||
bool touch = true;
|
|
||||||
float deadzone = 0.15f;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 平台后端工厂
|
* @brief 平台后端工厂
|
||||||
* 用于注册和创建平台后端
|
* 用于注册和创建平台后端
|
||||||
|
|
@ -95,4 +85,4 @@ private:
|
||||||
} e2d_backend_reg_##name; \
|
} e2d_backend_reg_##name; \
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <extra2d/core/color.h>
|
#include <extra2d/core/color.h>
|
||||||
|
#include <extra2d/core/export.h>
|
||||||
#include <extra2d/core/math_types.h>
|
#include <extra2d/core/math_types.h>
|
||||||
#include <extra2d/core/types.h>
|
#include <extra2d/core/types.h>
|
||||||
#include <extra2d/event/event_dispatcher.h>
|
#include <extra2d/event/event_dispatcher.h>
|
||||||
|
|
@ -19,7 +20,7 @@ struct RenderCommand;
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 节点基类 - 场景图的基础
|
// 节点基类 - 场景图的基础
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class Node : public std::enable_shared_from_this<Node> {
|
class E2D_API Node : public std::enable_shared_from_this<Node> {
|
||||||
public:
|
public:
|
||||||
Node();
|
Node();
|
||||||
virtual ~Node();
|
virtual ~Node();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <extra2d/core/color.h>
|
#include <extra2d/core/color.h>
|
||||||
|
#include <extra2d/core/export.h>
|
||||||
#include <extra2d/graphics/camera.h>
|
#include <extra2d/graphics/camera.h>
|
||||||
#include <extra2d/scene/node.h>
|
#include <extra2d/scene/node.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -13,7 +14,7 @@ struct RenderCommand;
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 场景类 - 节点容器,管理整个场景图
|
// 场景类 - 节点容器,管理整个场景图
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class Scene : public Node {
|
class E2D_API Scene : public Node {
|
||||||
public:
|
public:
|
||||||
Scene();
|
Scene();
|
||||||
~Scene() override = default;
|
~Scene() override = default;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <extra2d/core/color.h>
|
#include <extra2d/core/color.h>
|
||||||
|
#include <extra2d/core/export.h>
|
||||||
#include <extra2d/core/math_types.h>
|
#include <extra2d/core/math_types.h>
|
||||||
#include <extra2d/scene/node.h>
|
#include <extra2d/scene/node.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -15,7 +16,7 @@ enum class ShapeType { Point, Line, Rect, Circle, Triangle, Polygon };
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 形状节点 - 用于绘制几何形状
|
// 形状节点 - 用于绘制几何形状
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class ShapeNode : public Node {
|
class E2D_API ShapeNode : public Node {
|
||||||
public:
|
public:
|
||||||
ShapeNode();
|
ShapeNode();
|
||||||
~ShapeNode() override = default;
|
~ShapeNode() override = default;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <extra2d/core/export.h>
|
||||||
#include <extra2d/core/types.h>
|
#include <extra2d/core/types.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -128,7 +129,7 @@ inline std::string e2d_format(const char *fmt, const Args &...args) {
|
||||||
|
|
||||||
inline std::string e2d_format(const char *fmt) { return std::string(fmt); }
|
inline std::string e2d_format(const char *fmt) { return std::string(fmt); }
|
||||||
|
|
||||||
class Logger {
|
class E2D_API Logger {
|
||||||
public:
|
public:
|
||||||
static void init();
|
static void init();
|
||||||
static void shutdown();
|
static void shutdown();
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
#include <extra2d/app/application.h>
|
#include <extra2d/app/application.h>
|
||||||
|
#include <extra2d/core/module_meta.h>
|
||||||
#include <extra2d/graphics/vram_manager.h>
|
#include <extra2d/graphics/vram_manager.h>
|
||||||
#include <extra2d/modules/config_module.h>
|
#include <extra2d/modules/config_module.h>
|
||||||
#include <extra2d/modules/input_module.h>
|
#include <extra2d/modules/input_module.h>
|
||||||
#include <extra2d/modules/platform_module.h>
|
|
||||||
#include <extra2d/modules/render_module.h>
|
#include <extra2d/modules/render_module.h>
|
||||||
#include <extra2d/modules/window_module.h>
|
#include <extra2d/modules/window_module.h>
|
||||||
#include <extra2d/services/camera_service.h>
|
#include <extra2d/services/camera_service.h>
|
||||||
|
|
@ -11,9 +11,7 @@
|
||||||
#include <extra2d/services/timer_service.h>
|
#include <extra2d/services/timer_service.h>
|
||||||
#include <extra2d/utils/logger.h>
|
#include <extra2d/utils/logger.h>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -36,23 +34,6 @@ Application &Application::get() {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::use(Module &m) {
|
|
||||||
for (auto *existing : modules_) {
|
|
||||||
if (existing == &m) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
modules_.push_back(&m);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::use(std::initializer_list<Module *> modules) {
|
|
||||||
for (auto *m : modules) {
|
|
||||||
if (m) {
|
|
||||||
use(*m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Application::init() {
|
bool Application::init() {
|
||||||
AppConfig cfg;
|
AppConfig cfg;
|
||||||
return init(cfg);
|
return init(cfg);
|
||||||
|
|
@ -63,49 +44,14 @@ bool Application::init(const AppConfig &config) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!initCoreModules()) {
|
// 先注册核心服务,因为模块初始化时可能需要它们
|
||||||
|
registerCoreServices();
|
||||||
|
|
||||||
|
if (!initModules(config)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigModule *configModule = nullptr;
|
auto windowModule = getModule<WindowModule>();
|
||||||
for (auto *m : modules_) {
|
|
||||||
if (auto *cm = dynamic_cast<ConfigModule *>(m)) {
|
|
||||||
configModule = cm;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configModule) {
|
|
||||||
configModule->setAppConfig(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(modules_.begin(), modules_.end(), modulePriorityCompare);
|
|
||||||
|
|
||||||
WindowModule *windowModule = nullptr;
|
|
||||||
InputModule *inputModule = nullptr;
|
|
||||||
RenderModule *renderModule = nullptr;
|
|
||||||
|
|
||||||
for (auto *m : modules_) {
|
|
||||||
if (auto *wm = dynamic_cast<WindowModule *>(m)) {
|
|
||||||
windowModule = wm;
|
|
||||||
}
|
|
||||||
if (auto *im = dynamic_cast<InputModule *>(m)) {
|
|
||||||
inputModule = im;
|
|
||||||
}
|
|
||||||
if (auto *rm = dynamic_cast<RenderModule *>(m)) {
|
|
||||||
renderModule = rm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto *m : modules_) {
|
|
||||||
if (m == inputModule || m == renderModule) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!m->isInitialized()) {
|
|
||||||
m->setupModule();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!windowModule || !windowModule->isInitialized()) {
|
if (!windowModule || !windowModule->isInitialized()) {
|
||||||
E2D_LOG_ERROR("Window module not initialized");
|
E2D_LOG_ERROR("Window module not initialized");
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -117,18 +63,19 @@ bool Application::init(const AppConfig &config) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerCoreServices();
|
auto inputModule = getModule<InputModule>();
|
||||||
|
|
||||||
if (inputModule) {
|
if (inputModule) {
|
||||||
inputModule->setWindow(window_);
|
inputModule->setWindow(window_);
|
||||||
inputModule->setupModule();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto renderModule = getModule<RenderModule>();
|
||||||
if (renderModule) {
|
if (renderModule) {
|
||||||
renderModule->setWindow(window_);
|
renderModule->setWindow(window_);
|
||||||
renderModule->setupModule();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 注册 CameraService(需要 window)
|
||||||
|
registerCameraService();
|
||||||
|
|
||||||
if (!ServiceLocator::instance().initializeAll()) {
|
if (!ServiceLocator::instance().initializeAll()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -162,168 +109,29 @@ bool Application::init(const std::string &configPath) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!initCoreModules()) {
|
auto configModule = getModule<ConfigModule>();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigModule *configModule = nullptr;
|
|
||||||
for (auto *m : modules_) {
|
|
||||||
if (auto *cm = dynamic_cast<ConfigModule *>(m)) {
|
|
||||||
configModule = cm;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configModule) {
|
if (configModule) {
|
||||||
configModule->setConfigPath(configPath);
|
configModule->setConfigPath(configPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(modules_.begin(), modules_.end(), modulePriorityCompare);
|
AppConfig cfg;
|
||||||
|
return init(cfg);
|
||||||
WindowModule *windowModule = nullptr;
|
|
||||||
InputModule *inputModule = nullptr;
|
|
||||||
RenderModule *renderModule = nullptr;
|
|
||||||
|
|
||||||
for (auto *m : modules_) {
|
|
||||||
if (auto *wm = dynamic_cast<WindowModule *>(m)) {
|
|
||||||
windowModule = wm;
|
|
||||||
}
|
|
||||||
if (auto *im = dynamic_cast<InputModule *>(m)) {
|
|
||||||
inputModule = im;
|
|
||||||
}
|
|
||||||
if (auto *rm = dynamic_cast<RenderModule *>(m)) {
|
|
||||||
renderModule = rm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto *m : modules_) {
|
|
||||||
if (m == inputModule || m == renderModule) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!m->isInitialized()) {
|
|
||||||
m->setupModule();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!windowModule || !windowModule->isInitialized()) {
|
|
||||||
E2D_LOG_ERROR("Window module not initialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
window_ = windowModule->getWindow();
|
|
||||||
if (!window_) {
|
|
||||||
E2D_LOG_ERROR("Window not created");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
registerCoreServices();
|
|
||||||
|
|
||||||
if (inputModule) {
|
|
||||||
inputModule->setWindow(window_);
|
|
||||||
inputModule->setupModule();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (renderModule) {
|
|
||||||
renderModule->setWindow(window_);
|
|
||||||
renderModule->setupModule();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::initCoreModules() {
|
bool Application::initModules(const AppConfig &config) {
|
||||||
bool hasConfig = false;
|
auto configModule = getModule<ConfigModule>();
|
||||||
bool hasPlatform = false;
|
if (configModule) {
|
||||||
bool hasWindow = false;
|
configModule->setAppConfig(config);
|
||||||
bool hasInput = false;
|
|
||||||
bool hasRender = false;
|
|
||||||
|
|
||||||
for (auto *m : modules_) {
|
|
||||||
if (dynamic_cast<ConfigModule *>(m))
|
|
||||||
hasConfig = true;
|
|
||||||
if (dynamic_cast<PlatformModule *>(m))
|
|
||||||
hasPlatform = true;
|
|
||||||
if (dynamic_cast<WindowModule *>(m))
|
|
||||||
hasWindow = true;
|
|
||||||
if (dynamic_cast<InputModule *>(m))
|
|
||||||
hasInput = true;
|
|
||||||
if (dynamic_cast<RenderModule *>(m))
|
|
||||||
hasRender = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasConfig) {
|
if (!ModuleRegistry::instance().createAndInitAll()) {
|
||||||
static ConfigModule defaultConfigModule;
|
E2D_LOG_ERROR("Failed to initialize modules");
|
||||||
use(defaultConfigModule);
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasPlatform) {
|
|
||||||
static PlatformModule defaultPlatformModule;
|
|
||||||
use(defaultPlatformModule);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasWindow) {
|
|
||||||
static WindowModule defaultWindowModule;
|
|
||||||
use(defaultWindowModule);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasInput) {
|
|
||||||
static InputModule defaultInputModule;
|
|
||||||
use(defaultInputModule);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasRender) {
|
|
||||||
static RenderModule defaultRenderModule;
|
|
||||||
use(defaultRenderModule);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::setupAllModules() {
|
|
||||||
std::sort(modules_.begin(), modules_.end(), modulePriorityCompare);
|
|
||||||
|
|
||||||
for (auto *m : modules_) {
|
|
||||||
if (!m->isInitialized()) {
|
|
||||||
m->setupModule();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::destroyAllModules() {
|
|
||||||
for (auto it = modules_.rbegin(); it != modules_.rend(); ++it) {
|
|
||||||
Module *m = *it;
|
|
||||||
if (m->isInitialized()) {
|
|
||||||
m->destroyModule();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
modules_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::registerCoreServices() {
|
void Application::registerCoreServices() {
|
||||||
auto &locator = ServiceLocator::instance();
|
auto &locator = ServiceLocator::instance();
|
||||||
|
|
||||||
|
|
@ -338,6 +146,10 @@ void Application::registerCoreServices() {
|
||||||
if (!locator.hasService<ITimerService>()) {
|
if (!locator.hasService<ITimerService>()) {
|
||||||
locator.registerService<ITimerService>(makeShared<TimerService>());
|
locator.registerService<ITimerService>(makeShared<TimerService>());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::registerCameraService() {
|
||||||
|
auto &locator = ServiceLocator::instance();
|
||||||
|
|
||||||
if (!locator.hasService<ICameraService>()) {
|
if (!locator.hasService<ICameraService>()) {
|
||||||
auto cameraService = makeShared<CameraService>();
|
auto cameraService = makeShared<CameraService>();
|
||||||
|
|
@ -359,16 +171,20 @@ void Application::shutdown() {
|
||||||
if (!initialized_)
|
if (!initialized_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
E2D_LOG_INFO("Application shutting down...");
|
||||||
|
|
||||||
VRAMMgr::get().printStats();
|
VRAMMgr::get().printStats();
|
||||||
|
|
||||||
|
// 先销毁模块(它们可能需要服务和窗口)
|
||||||
|
ModuleRegistry::instance().destroyAll();
|
||||||
|
|
||||||
|
// 最后清除服务
|
||||||
ServiceLocator::instance().clear();
|
ServiceLocator::instance().clear();
|
||||||
|
|
||||||
window_ = nullptr;
|
|
||||||
|
|
||||||
destroyAllModules();
|
|
||||||
|
|
||||||
initialized_ = false;
|
initialized_ = false;
|
||||||
running_ = false;
|
running_ = false;
|
||||||
|
|
||||||
|
E2D_LOG_INFO("Application shutdown complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application() {
|
Application::~Application() {
|
||||||
|
|
@ -387,6 +203,9 @@ void Application::run() {
|
||||||
while (running_ && !window_->shouldClose()) {
|
while (running_ && !window_->shouldClose()) {
|
||||||
mainLoop();
|
mainLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 主循环结束后自动清理
|
||||||
|
shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::quit() {
|
void Application::quit() {
|
||||||
|
|
@ -437,14 +256,7 @@ void Application::mainLoop() {
|
||||||
|
|
||||||
render();
|
render();
|
||||||
|
|
||||||
RenderModule *renderModule = nullptr;
|
auto renderModule = getModule<RenderModule>();
|
||||||
for (auto *m : modules_) {
|
|
||||||
if (auto *rm = dynamic_cast<RenderModule *>(m)) {
|
|
||||||
renderModule = rm;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(void)renderModule;
|
(void)renderModule;
|
||||||
|
|
||||||
ConfigManager::instance().update(deltaTime_);
|
ConfigManager::instance().update(deltaTime_);
|
||||||
|
|
@ -453,19 +265,18 @@ void Application::mainLoop() {
|
||||||
void Application::update() {
|
void Application::update() {
|
||||||
ServiceLocator::instance().updateAll(deltaTime_);
|
ServiceLocator::instance().updateAll(deltaTime_);
|
||||||
|
|
||||||
auto ctx = UpdateContext(modules_, deltaTime_);
|
auto modules = ModuleRegistry::instance().getAllModules();
|
||||||
if (!modules_.empty()) {
|
auto ctx = UpdateContext(modules, deltaTime_);
|
||||||
|
if (!modules.empty()) {
|
||||||
ctx.next();
|
ctx.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::render() {
|
void Application::render() {
|
||||||
RenderBackend *renderer = nullptr;
|
RenderBackend *renderer = nullptr;
|
||||||
for (auto *m : modules_) {
|
auto renderModule = getModule<RenderModule>();
|
||||||
if (auto *rm = dynamic_cast<RenderModule *>(m)) {
|
if (renderModule) {
|
||||||
renderer = rm->getRenderer();
|
renderer = renderModule->getRenderer();
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!renderer) {
|
if (!renderer) {
|
||||||
|
|
@ -484,16 +295,18 @@ void Application::render() {
|
||||||
renderer->setViewport(0, 0, window_->width(), window_->height());
|
renderer->setViewport(0, 0, window_->width(), window_->height());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto modules = ModuleRegistry::instance().getAllModules();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto ctx = RenderContext(modules_, RenderContext::Phase::Before);
|
auto ctx = RenderContext(modules, RenderContext::Phase::Before);
|
||||||
if (!modules_.empty()) {
|
if (!modules.empty()) {
|
||||||
ctx.next();
|
ctx.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto ctx = RenderContext(modules_, RenderContext::Phase::On);
|
auto ctx = RenderContext(modules, RenderContext::Phase::On);
|
||||||
if (!modules_.empty()) {
|
if (!modules.empty()) {
|
||||||
ctx.next();
|
ctx.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -504,8 +317,8 @@ void Application::render() {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto ctx = RenderContext(modules_, RenderContext::Phase::After);
|
auto ctx = RenderContext(modules, RenderContext::Phase::After);
|
||||||
if (!modules_.empty()) {
|
if (!modules.empty()) {
|
||||||
ctx.next();
|
ctx.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -514,12 +327,9 @@ void Application::render() {
|
||||||
IInput &Application::input() { return *window_->input(); }
|
IInput &Application::input() { return *window_->input(); }
|
||||||
|
|
||||||
RenderBackend &Application::renderer() {
|
RenderBackend &Application::renderer() {
|
||||||
for (auto *m : modules_) {
|
auto renderModule = getModule<RenderModule>();
|
||||||
if (auto *rm = dynamic_cast<RenderModule *>(m)) {
|
if (renderModule && renderModule->getRenderer()) {
|
||||||
if (rm->getRenderer()) {
|
return *renderModule->getRenderer();
|
||||||
return *rm->getRenderer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
static RenderBackend *dummy = nullptr;
|
static RenderBackend *dummy = nullptr;
|
||||||
if (!dummy) {
|
if (!dummy) {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ AppConfig AppConfig::createDefault() {
|
||||||
config.appVersion = "1.0.0";
|
config.appVersion = "1.0.0";
|
||||||
config.organization = "";
|
config.organization = "";
|
||||||
config.configFile = "config.json";
|
config.configFile = "config.json";
|
||||||
config.targetPlatform = PlatformType::Auto;
|
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
@ -50,9 +49,6 @@ void AppConfig::merge(const AppConfig& other) {
|
||||||
if (other.configFile != "config.json") {
|
if (other.configFile != "config.json") {
|
||||||
configFile = other.configFile;
|
configFile = other.configFile;
|
||||||
}
|
}
|
||||||
if (other.targetPlatform != PlatformType::Auto) {
|
|
||||||
targetPlatform = other.targetPlatform;
|
|
||||||
}
|
|
||||||
|
|
||||||
E2D_LOG_INFO("Merged app config");
|
E2D_LOG_INFO("Merged app config");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,6 @@
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 去除字符串首尾空白字符
|
|
||||||
* @param str 输入字符串
|
|
||||||
* @return 去除空白后的字符串
|
|
||||||
*/
|
|
||||||
static std::string trim(const std::string &str) {
|
static std::string trim(const std::string &str) {
|
||||||
size_t start = 0;
|
size_t start = 0;
|
||||||
while (start < str.length() &&
|
while (start < str.length() &&
|
||||||
|
|
@ -28,11 +23,6 @@ static std::string trim(const std::string &str) {
|
||||||
return str.substr(start, end - start);
|
return str.substr(start, end - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 将字符串转换为小写
|
|
||||||
* @param str 输入字符串
|
|
||||||
* @return 小写字符串
|
|
||||||
*/
|
|
||||||
static std::string toLower(const std::string &str) {
|
static std::string toLower(const std::string &str) {
|
||||||
std::string result = str;
|
std::string result = str;
|
||||||
for (char &c : result) {
|
for (char &c : result) {
|
||||||
|
|
@ -41,17 +31,8 @@ static std::string toLower(const std::string &str) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief INI 数据存储结构
|
|
||||||
*/
|
|
||||||
using IniData = std::map<std::string, std::map<std::string, std::string>>;
|
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,
|
static ConfigLoadResult parseIniContent(const std::string &content,
|
||||||
IniData &data) {
|
IniData &data) {
|
||||||
std::istringstream stream(content);
|
std::istringstream stream(content);
|
||||||
|
|
@ -97,14 +78,6 @@ static ConfigLoadResult parseIniContent(const std::string &content,
|
||||||
return ConfigLoadResult::ok();
|
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 §ion,
|
static std::string getIniValue(const IniData &data, const std::string §ion,
|
||||||
const std::string &key,
|
const std::string &key,
|
||||||
const std::string &defaultValue = "") {
|
const std::string &defaultValue = "") {
|
||||||
|
|
@ -119,13 +92,6 @@ static std::string getIniValue(const IniData &data, const std::string §ion,
|
||||||
return keyIt->second;
|
return keyIt->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查 INI 值是否存在
|
|
||||||
* @param data INI 数据
|
|
||||||
* @param section 节名
|
|
||||||
* @param key 键名
|
|
||||||
* @return 是否存在
|
|
||||||
*/
|
|
||||||
static bool hasIniValue(const IniData &data, const std::string §ion,
|
static bool hasIniValue(const IniData &data, const std::string §ion,
|
||||||
const std::string &key) {
|
const std::string &key) {
|
||||||
auto sectionIt = data.find(section);
|
auto sectionIt = data.find(section);
|
||||||
|
|
@ -192,14 +158,6 @@ ConfigLoadResult IniConfigLoader::loadFromString(const std::string &content,
|
||||||
if (hasIniValue(data, "app", "configFile")) {
|
if (hasIniValue(data, "app", "configFile")) {
|
||||||
config.configFile = getIniValue(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 应用配置加载成功");
|
E2D_LOG_INFO("INI 应用配置加载成功");
|
||||||
return ConfigLoadResult::ok();
|
return ConfigLoadResult::ok();
|
||||||
|
|
@ -213,7 +171,6 @@ std::string IniConfigLoader::saveToString(const AppConfig &config) {
|
||||||
oss << "version=" << config.appVersion << "\n";
|
oss << "version=" << config.appVersion << "\n";
|
||||||
oss << "organization=" << config.organization << "\n";
|
oss << "organization=" << config.organization << "\n";
|
||||||
oss << "configFile=" << config.configFile << "\n";
|
oss << "configFile=" << config.configFile << "\n";
|
||||||
oss << "targetPlatform=" << static_cast<int>(config.targetPlatform) << "\n";
|
|
||||||
|
|
||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
@ -329,4 +286,4 @@ ConfigLoadResult IniConfigLoader::parseBool(const std::string &value,
|
||||||
return ConfigLoadResult::error("无法解析布尔值: " + value, -1, fieldName);
|
return ConfigLoadResult::error("无法解析布尔值: " + value, -1, fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,10 +71,6 @@ ConfigLoadResult JsonConfigLoader::loadFromString(const std::string& content, Ap
|
||||||
config.configFile = root["configFile"].get<std::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 应用配置加载成功");
|
E2D_LOG_INFO("JSON 应用配置加载成功");
|
||||||
return ConfigLoadResult::ok();
|
return ConfigLoadResult::ok();
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +82,6 @@ std::string JsonConfigLoader::saveToString(const AppConfig& config) {
|
||||||
root["appVersion"] = config.appVersion;
|
root["appVersion"] = config.appVersion;
|
||||||
root["organization"] = config.organization;
|
root["organization"] = config.organization;
|
||||||
root["configFile"] = config.configFile;
|
root["configFile"] = config.configFile;
|
||||||
root["targetPlatform"] = static_cast<int>(config.targetPlatform);
|
|
||||||
|
|
||||||
return root.dump(4);
|
return root.dump(4);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
#include <extra2d/config/config_manager.h>
|
#include <extra2d/config/config_manager.h>
|
||||||
#include <extra2d/config/platform_config.h>
|
|
||||||
#include <extra2d/config/platform_detector.h>
|
|
||||||
#include <extra2d/utils/logger.h>
|
#include <extra2d/utils/logger.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
|
@ -36,12 +34,6 @@ bool ConfigManager::initialize(const std::string& configPath) {
|
||||||
|
|
||||||
m_configPath = configPath;
|
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>();
|
m_loader = makeUnique<JsonConfigLoader>();
|
||||||
if (!m_loader) {
|
if (!m_loader) {
|
||||||
E2D_LOG_ERROR("Failed to create config loader");
|
E2D_LOG_ERROR("Failed to create config loader");
|
||||||
|
|
@ -49,13 +41,11 @@ bool ConfigManager::initialize(const std::string& configPath) {
|
||||||
}
|
}
|
||||||
|
|
||||||
m_appConfig = AppConfig::createDefault();
|
m_appConfig = AppConfig::createDefault();
|
||||||
m_appConfig.targetPlatform = PlatformDetector::detect();
|
|
||||||
|
|
||||||
m_initialized = true;
|
m_initialized = true;
|
||||||
m_modified = false;
|
m_modified = false;
|
||||||
|
|
||||||
E2D_LOG_INFO("ConfigManager initialized for platform: {}",
|
E2D_LOG_INFO("ConfigManager initialized");
|
||||||
m_platformConfig->platformName());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,7 +63,6 @@ void ConfigManager::shutdown() {
|
||||||
m_changeCallbacks.clear();
|
m_changeCallbacks.clear();
|
||||||
m_rawValues.clear();
|
m_rawValues.clear();
|
||||||
m_loader.reset();
|
m_loader.reset();
|
||||||
m_platformConfig.reset();
|
|
||||||
|
|
||||||
m_initialized = false;
|
m_initialized = false;
|
||||||
m_modified = false;
|
m_modified = false;
|
||||||
|
|
@ -217,14 +206,6 @@ void ConfigManager::setAppConfig(const AppConfig& config) {
|
||||||
E2D_LOG_INFO("App config updated");
|
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) {
|
int ConfigManager::registerChangeCallback(ConfigChangeCallback callback) {
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,224 +0,0 @@
|
||||||
#include <extra2d/config/app_config.h>
|
|
||||||
#include <extra2d/config/platform_config.h>
|
|
||||||
#include <extra2d/utils/logger.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __SWITCH__
|
|
||||||
#include <switch.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class WindowsPlatformConfig : public PlatformConfig {
|
|
||||||
public:
|
|
||||||
WindowsPlatformConfig() {
|
|
||||||
caps_.supportsWindowed = true;
|
|
||||||
caps_.supportsFullscreen = true;
|
|
||||||
caps_.supportsBorderless = true;
|
|
||||||
caps_.supportsCursor = true;
|
|
||||||
caps_.supportsCursorHide = true;
|
|
||||||
caps_.supportsDPIAwareness = true;
|
|
||||||
caps_.supportsVSync = true;
|
|
||||||
caps_.supportsMultiMonitor = true;
|
|
||||||
caps_.supportsClipboard = true;
|
|
||||||
caps_.supportsGamepad = true;
|
|
||||||
caps_.supportsTouch = false;
|
|
||||||
caps_.supportsKeyboard = true;
|
|
||||||
caps_.supportsMouse = true;
|
|
||||||
caps_.supportsResize = true;
|
|
||||||
caps_.supportsHighDPI = true;
|
|
||||||
caps_.maxTextureSize = 16384;
|
|
||||||
caps_.preferredScreenWidth = 1920;
|
|
||||||
caps_.preferredScreenHeight = 1080;
|
|
||||||
caps_.defaultDPI = 96.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformType platformType() const override { return PlatformType::Windows; }
|
|
||||||
const char *platformName() const override { return "Windows"; }
|
|
||||||
const PlatformCapabilities &capabilities() const override { return caps_; }
|
|
||||||
|
|
||||||
int getRecommendedWidth() const override { return 1920; }
|
|
||||||
int getRecommendedHeight() const override { return 1080; }
|
|
||||||
bool isResolutionSupported(int width, int height) const override {
|
|
||||||
return width >= 320 && height >= 240 && width <= caps_.maxTextureSize &&
|
|
||||||
height <= caps_.maxTextureSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
PlatformCapabilities caps_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class LinuxPlatformConfig : public PlatformConfig {
|
|
||||||
public:
|
|
||||||
LinuxPlatformConfig() {
|
|
||||||
caps_.supportsWindowed = true;
|
|
||||||
caps_.supportsFullscreen = true;
|
|
||||||
caps_.supportsBorderless = true;
|
|
||||||
caps_.supportsCursor = true;
|
|
||||||
caps_.supportsCursorHide = true;
|
|
||||||
caps_.supportsDPIAwareness = true;
|
|
||||||
caps_.supportsVSync = true;
|
|
||||||
caps_.supportsMultiMonitor = true;
|
|
||||||
caps_.supportsClipboard = true;
|
|
||||||
caps_.supportsGamepad = true;
|
|
||||||
caps_.supportsTouch = false;
|
|
||||||
caps_.supportsKeyboard = true;
|
|
||||||
caps_.supportsMouse = true;
|
|
||||||
caps_.supportsResize = true;
|
|
||||||
caps_.supportsHighDPI = true;
|
|
||||||
caps_.maxTextureSize = 16384;
|
|
||||||
caps_.preferredScreenWidth = 1920;
|
|
||||||
caps_.preferredScreenHeight = 1080;
|
|
||||||
caps_.defaultDPI = 96.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformType platformType() const override { return PlatformType::Linux; }
|
|
||||||
const char *platformName() const override { return "Linux"; }
|
|
||||||
const PlatformCapabilities &capabilities() const override { return caps_; }
|
|
||||||
|
|
||||||
int getRecommendedWidth() const override { return 1920; }
|
|
||||||
int getRecommendedHeight() const override { return 1080; }
|
|
||||||
bool isResolutionSupported(int width, int height) const override {
|
|
||||||
return width >= 320 && height >= 240;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
PlatformCapabilities caps_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MacOSPlatformConfig : public PlatformConfig {
|
|
||||||
public:
|
|
||||||
MacOSPlatformConfig() {
|
|
||||||
caps_.supportsWindowed = true;
|
|
||||||
caps_.supportsFullscreen = true;
|
|
||||||
caps_.supportsBorderless = true;
|
|
||||||
caps_.supportsCursor = true;
|
|
||||||
caps_.supportsCursorHide = true;
|
|
||||||
caps_.supportsDPIAwareness = true;
|
|
||||||
caps_.supportsVSync = true;
|
|
||||||
caps_.supportsMultiMonitor = true;
|
|
||||||
caps_.supportsClipboard = true;
|
|
||||||
caps_.supportsGamepad = true;
|
|
||||||
caps_.supportsTouch = false;
|
|
||||||
caps_.supportsKeyboard = true;
|
|
||||||
caps_.supportsMouse = true;
|
|
||||||
caps_.supportsResize = true;
|
|
||||||
caps_.supportsHighDPI = true;
|
|
||||||
caps_.maxTextureSize = 16384;
|
|
||||||
caps_.preferredScreenWidth = 1920;
|
|
||||||
caps_.preferredScreenHeight = 1080;
|
|
||||||
caps_.defaultDPI = 144.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformType platformType() const override { return PlatformType::macOS; }
|
|
||||||
const char *platformName() const override { return "macOS"; }
|
|
||||||
const PlatformCapabilities &capabilities() const override { return caps_; }
|
|
||||||
|
|
||||||
int getRecommendedWidth() const override { return 1920; }
|
|
||||||
int getRecommendedHeight() const override { return 1080; }
|
|
||||||
bool isResolutionSupported(int width, int height) const override {
|
|
||||||
return width >= 320 && height >= 240;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
PlatformCapabilities caps_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SwitchPlatformConfig : public PlatformConfig {
|
|
||||||
public:
|
|
||||||
SwitchPlatformConfig() {
|
|
||||||
caps_.supportsWindowed = false;
|
|
||||||
caps_.supportsFullscreen = true;
|
|
||||||
caps_.supportsBorderless = false;
|
|
||||||
caps_.supportsCursor = false;
|
|
||||||
caps_.supportsCursorHide = false;
|
|
||||||
caps_.supportsDPIAwareness = false;
|
|
||||||
caps_.supportsVSync = true;
|
|
||||||
caps_.supportsMultiMonitor = false;
|
|
||||||
caps_.supportsClipboard = false;
|
|
||||||
caps_.supportsGamepad = true;
|
|
||||||
caps_.supportsTouch = true;
|
|
||||||
caps_.supportsKeyboard = false;
|
|
||||||
caps_.supportsMouse = false;
|
|
||||||
caps_.supportsResize = false;
|
|
||||||
caps_.supportsHighDPI = false;
|
|
||||||
caps_.maxTextureSize = 8192;
|
|
||||||
caps_.preferredScreenWidth = 1920;
|
|
||||||
caps_.preferredScreenHeight = 1080;
|
|
||||||
caps_.defaultDPI = 96.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformType platformType() const override { return PlatformType::Switch; }
|
|
||||||
const char *platformName() const override { return "Nintendo Switch"; }
|
|
||||||
const PlatformCapabilities &capabilities() const override { return caps_; }
|
|
||||||
|
|
||||||
int getRecommendedWidth() const override { return 1920; }
|
|
||||||
int getRecommendedHeight() const override { return 1080; }
|
|
||||||
bool isResolutionSupported(int width, int height) const override {
|
|
||||||
return (width == 1920 && height == 1080) ||
|
|
||||||
(width == 1280 && height == 720);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
PlatformCapabilities caps_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
UniquePtr<PlatformConfig> createPlatformConfig(PlatformType type) {
|
|
||||||
if (type == PlatformType::Auto) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
type = PlatformType::Windows;
|
|
||||||
#elif defined(__SWITCH__)
|
|
||||||
type = PlatformType::Switch;
|
|
||||||
#elif defined(__linux__)
|
|
||||||
type = PlatformType::Linux;
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
type = PlatformType::macOS;
|
|
||||||
#else
|
|
||||||
type = PlatformType::Windows;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case PlatformType::Windows:
|
|
||||||
E2D_LOG_INFO("Creating Windows platform config");
|
|
||||||
return makeUnique<WindowsPlatformConfig>();
|
|
||||||
case PlatformType::Switch:
|
|
||||||
E2D_LOG_INFO("Creating Nintendo Switch platform config");
|
|
||||||
return makeUnique<SwitchPlatformConfig>();
|
|
||||||
case PlatformType::Linux:
|
|
||||||
E2D_LOG_INFO("Creating Linux platform config");
|
|
||||||
return makeUnique<LinuxPlatformConfig>();
|
|
||||||
case PlatformType::macOS:
|
|
||||||
E2D_LOG_INFO("Creating macOS platform config");
|
|
||||||
return makeUnique<MacOSPlatformConfig>();
|
|
||||||
default:
|
|
||||||
E2D_LOG_WARN("Unknown platform type, defaulting to Windows");
|
|
||||||
return makeUnique<WindowsPlatformConfig>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *getPlatformTypeName(PlatformType type) {
|
|
||||||
switch (type) {
|
|
||||||
case PlatformType::Auto:
|
|
||||||
return "Auto";
|
|
||||||
case PlatformType::Windows:
|
|
||||||
return "Windows";
|
|
||||||
case PlatformType::Switch:
|
|
||||||
return "Switch";
|
|
||||||
case PlatformType::Linux:
|
|
||||||
return "Linux";
|
|
||||||
case PlatformType::macOS:
|
|
||||||
return "macOS";
|
|
||||||
default:
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,678 +0,0 @@
|
||||||
#include <extra2d/config/platform_detector.h>
|
|
||||||
#include <extra2d/config/platform_config.h>
|
|
||||||
#include <extra2d/utils/logger.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#include <shlobj.h>
|
|
||||||
#include <psapi.h>
|
|
||||||
#elif defined(__linux__)
|
|
||||||
#include <sys/sysinfo.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#include <cstdlib>
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
#include <sys/sysctl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#include <cstdlib>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __SWITCH__
|
|
||||||
#include <switch.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检测当前运行平台
|
|
||||||
* 使用编译时宏判断当前平台类型
|
|
||||||
* @return 当前平台的类型枚举值
|
|
||||||
*/
|
|
||||||
PlatformType PlatformDetector::detect() {
|
|
||||||
#ifdef _WIN32
|
|
||||||
return PlatformType::Windows;
|
|
||||||
#elif defined(__SWITCH__)
|
|
||||||
return PlatformType::Switch;
|
|
||||||
#elif defined(__linux__)
|
|
||||||
return PlatformType::Linux;
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
return PlatformType::macOS;
|
|
||||||
#else
|
|
||||||
return PlatformType::Windows;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取当前平台名称字符串
|
|
||||||
* @return 平台名称(如 "Windows", "Linux", "macOS", "Switch")
|
|
||||||
*/
|
|
||||||
const char* PlatformDetector::platformName() {
|
|
||||||
return platformName(detect());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取指定平台类型的名称
|
|
||||||
* @param type 平台类型
|
|
||||||
* @return 平台名称字符串
|
|
||||||
*/
|
|
||||||
const char* PlatformDetector::platformName(PlatformType type) {
|
|
||||||
switch (type) {
|
|
||||||
case PlatformType::Windows: return "Windows";
|
|
||||||
case PlatformType::Switch: return "Nintendo Switch";
|
|
||||||
case PlatformType::Linux: return "Linux";
|
|
||||||
case PlatformType::macOS: return "macOS";
|
|
||||||
case PlatformType::Auto: return "Auto";
|
|
||||||
default: return "Unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查当前平台是否为桌面平台
|
|
||||||
* @return 如果是桌面平台返回 true
|
|
||||||
*/
|
|
||||||
bool PlatformDetector::isDesktopPlatform() {
|
|
||||||
PlatformType type = detect();
|
|
||||||
return type == PlatformType::Windows ||
|
|
||||||
type == PlatformType::Linux ||
|
|
||||||
type == PlatformType::macOS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查当前平台是否为游戏主机平台
|
|
||||||
* @return 如果是游戏主机平台返回 true
|
|
||||||
*/
|
|
||||||
bool PlatformDetector::isConsolePlatform() {
|
|
||||||
return detect() == PlatformType::Switch;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查当前平台是否为移动平台
|
|
||||||
* @return 如果是移动平台返回 true
|
|
||||||
*/
|
|
||||||
bool PlatformDetector::isMobilePlatform() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取当前平台的能力
|
|
||||||
* @return 平台能力结构
|
|
||||||
*/
|
|
||||||
PlatformCapabilities PlatformDetector::capabilities() {
|
|
||||||
return capabilities(detect());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取指定平台的能力
|
|
||||||
* @param type 平台类型
|
|
||||||
* @return 平台能力结构
|
|
||||||
*/
|
|
||||||
PlatformCapabilities PlatformDetector::capabilities(PlatformType type) {
|
|
||||||
switch (type) {
|
|
||||||
case PlatformType::Windows:
|
|
||||||
return getWindowsCapabilities();
|
|
||||||
case PlatformType::Switch:
|
|
||||||
return getSwitchCapabilities();
|
|
||||||
case PlatformType::Linux:
|
|
||||||
return getLinuxCapabilities();
|
|
||||||
case PlatformType::macOS:
|
|
||||||
return getMacOSCapabilities();
|
|
||||||
default:
|
|
||||||
return getWindowsCapabilities();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取当前平台的默认配置
|
|
||||||
* @return 平台默认的应用配置
|
|
||||||
*/
|
|
||||||
AppConfig PlatformDetector::platformDefaults() {
|
|
||||||
return platformDefaults(detect());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取指定平台的默认配置
|
|
||||||
* @param type 平台类型
|
|
||||||
* @return 平台默认的应用配置
|
|
||||||
*/
|
|
||||||
AppConfig PlatformDetector::platformDefaults(PlatformType type) {
|
|
||||||
switch (type) {
|
|
||||||
case PlatformType::Windows:
|
|
||||||
return getWindowsDefaults();
|
|
||||||
case PlatformType::Switch:
|
|
||||||
return getSwitchDefaults();
|
|
||||||
case PlatformType::Linux:
|
|
||||||
return getLinuxDefaults();
|
|
||||||
case PlatformType::macOS:
|
|
||||||
return getMacOSDefaults();
|
|
||||||
default:
|
|
||||||
return AppConfig::createDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取当前平台的推荐分辨率
|
|
||||||
* @param width 输出宽度
|
|
||||||
* @param height 输出高度
|
|
||||||
*/
|
|
||||||
void PlatformDetector::getRecommendedResolution(int& width, int& height) {
|
|
||||||
PlatformCapabilities caps = capabilities();
|
|
||||||
width = caps.preferredScreenWidth;
|
|
||||||
height = caps.preferredScreenHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取当前平台的默认 DPI
|
|
||||||
* @return 默认 DPI 值
|
|
||||||
*/
|
|
||||||
float PlatformDetector::getDefaultDPI() {
|
|
||||||
return capabilities().defaultDPI;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查当前平台是否支持指定功能
|
|
||||||
* @param feature 功能名称
|
|
||||||
* @return 如果支持返回 true
|
|
||||||
*/
|
|
||||||
bool PlatformDetector::supportsFeature(const std::string& feature) {
|
|
||||||
PlatformCapabilities caps = capabilities();
|
|
||||||
|
|
||||||
if (feature == "windowed") return caps.supportsWindowed;
|
|
||||||
if (feature == "fullscreen") return caps.supportsFullscreen;
|
|
||||||
if (feature == "borderless") return caps.supportsBorderless;
|
|
||||||
if (feature == "cursor") return caps.supportsCursor;
|
|
||||||
if (feature == "cursor_hide") return caps.supportsCursorHide;
|
|
||||||
if (feature == "dpi_awareness") return caps.supportsDPIAwareness;
|
|
||||||
if (feature == "vsync") return caps.supportsVSync;
|
|
||||||
if (feature == "multi_monitor") return caps.supportsMultiMonitor;
|
|
||||||
if (feature == "clipboard") return caps.supportsClipboard;
|
|
||||||
if (feature == "gamepad") return caps.supportsGamepad;
|
|
||||||
if (feature == "touch") return caps.supportsTouch;
|
|
||||||
if (feature == "keyboard") return caps.supportsKeyboard;
|
|
||||||
if (feature == "mouse") return caps.supportsMouse;
|
|
||||||
if (feature == "resize") return caps.supportsResize;
|
|
||||||
if (feature == "high_dpi") return caps.supportsHighDPI;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取系统内存大小
|
|
||||||
* @return 系统内存大小(MB),如果无法获取返回 0
|
|
||||||
*/
|
|
||||||
int PlatformDetector::getSystemMemoryMB() {
|
|
||||||
#ifdef _WIN32
|
|
||||||
MEMORYSTATUSEX status;
|
|
||||||
status.dwLength = sizeof(status);
|
|
||||||
if (GlobalMemoryStatusEx(&status)) {
|
|
||||||
return static_cast<int>(status.ullTotalPhys / (1024 * 1024));
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
#elif defined(__SWITCH__)
|
|
||||||
return 4096;
|
|
||||||
#elif defined(__linux__)
|
|
||||||
struct sysinfo info;
|
|
||||||
if (sysinfo(&info) == 0) {
|
|
||||||
return static_cast<int>(info.totalram * info.mem_unit / (1024 * 1024));
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
int mib[2] = {CTL_HW, HW_MEMSIZE};
|
|
||||||
int64_t memSize = 0;
|
|
||||||
size_t length = sizeof(memSize);
|
|
||||||
if (sysctl(mib, 2, &memSize, &length, nullptr, 0) == 0) {
|
|
||||||
return static_cast<int>(memSize / (1024 * 1024));
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取 CPU 核心数
|
|
||||||
* @return CPU 核心数
|
|
||||||
*/
|
|
||||||
int PlatformDetector::getCPUCoreCount() {
|
|
||||||
#ifdef _WIN32
|
|
||||||
SYSTEM_INFO sysinfo;
|
|
||||||
GetSystemInfo(&sysinfo);
|
|
||||||
return static_cast<int>(sysinfo.dwNumberOfProcessors);
|
|
||||||
#elif defined(__SWITCH__)
|
|
||||||
return 4;
|
|
||||||
#elif defined(__linux__) || defined(__APPLE__)
|
|
||||||
long cores = sysconf(_SC_NPROCESSORS_ONLN);
|
|
||||||
return static_cast<int>(cores > 0 ? cores : 1);
|
|
||||||
#else
|
|
||||||
return 1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查是否支持多线程渲染
|
|
||||||
* @return 如果支持返回 true
|
|
||||||
*/
|
|
||||||
bool PlatformDetector::supportsMultithreadedRendering() {
|
|
||||||
#ifdef __SWITCH__
|
|
||||||
return false;
|
|
||||||
#else
|
|
||||||
return getCPUCoreCount() >= 2;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台特定的配置路径
|
|
||||||
* @param appName 应用名称
|
|
||||||
* @return 配置文件目录路径
|
|
||||||
*/
|
|
||||||
std::string PlatformDetector::getConfigPath(const std::string& appName) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
char path[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(nullptr, CSIDL_APPDATA, nullptr, 0, path))) {
|
|
||||||
return std::string(path) + "\\" + appName + "\\config";
|
|
||||||
}
|
|
||||||
return ".\\config";
|
|
||||||
#elif defined(__SWITCH__)
|
|
||||||
return "sdmc:/config/" + appName;
|
|
||||||
#elif defined(__linux__)
|
|
||||||
const char* configHome = getenv("XDG_CONFIG_HOME");
|
|
||||||
if (configHome && configHome[0] != '\0') {
|
|
||||||
return std::string(configHome) + "/" + appName;
|
|
||||||
}
|
|
||||||
const char* home = getenv("HOME");
|
|
||||||
if (!home) {
|
|
||||||
struct passwd* pwd = getpwuid(getuid());
|
|
||||||
if (pwd) home = pwd->pw_dir;
|
|
||||||
}
|
|
||||||
if (home) {
|
|
||||||
return std::string(home) + "/.config/" + appName;
|
|
||||||
}
|
|
||||||
return "./config";
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
const char* home = getenv("HOME");
|
|
||||||
if (!home) {
|
|
||||||
struct passwd* pwd = getpwuid(getuid());
|
|
||||||
if (pwd) home = pwd->pw_dir;
|
|
||||||
}
|
|
||||||
if (home) {
|
|
||||||
return std::string(home) + "/Library/Application Support/" + appName + "/config";
|
|
||||||
}
|
|
||||||
return "./config";
|
|
||||||
#else
|
|
||||||
return "./config";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台特定的存档路径
|
|
||||||
* @param appName 应用名称
|
|
||||||
* @return 存档文件目录路径
|
|
||||||
*/
|
|
||||||
std::string PlatformDetector::getSavePath(const std::string& appName) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
char path[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(nullptr, CSIDL_APPDATA, nullptr, 0, path))) {
|
|
||||||
return std::string(path) + "\\" + appName + "\\saves";
|
|
||||||
}
|
|
||||||
return ".\\saves";
|
|
||||||
#elif defined(__SWITCH__)
|
|
||||||
return "sdmc:/saves/" + appName;
|
|
||||||
#elif defined(__linux__)
|
|
||||||
const char* dataHome = getenv("XDG_DATA_HOME");
|
|
||||||
if (dataHome && dataHome[0] != '\0') {
|
|
||||||
return std::string(dataHome) + "/" + appName + "/saves";
|
|
||||||
}
|
|
||||||
const char* home = getenv("HOME");
|
|
||||||
if (!home) {
|
|
||||||
struct passwd* pwd = getpwuid(getuid());
|
|
||||||
if (pwd) home = pwd->pw_dir;
|
|
||||||
}
|
|
||||||
if (home) {
|
|
||||||
return std::string(home) + "/.local/share/" + appName + "/saves";
|
|
||||||
}
|
|
||||||
return "./saves";
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
const char* home = getenv("HOME");
|
|
||||||
if (!home) {
|
|
||||||
struct passwd* pwd = getpwuid(getuid());
|
|
||||||
if (pwd) home = pwd->pw_dir;
|
|
||||||
}
|
|
||||||
if (home) {
|
|
||||||
return std::string(home) + "/Library/Application Support/" + appName + "/saves";
|
|
||||||
}
|
|
||||||
return "./saves";
|
|
||||||
#else
|
|
||||||
return "./saves";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台特定的缓存路径
|
|
||||||
* @param appName 应用名称
|
|
||||||
* @return 缓存文件目录路径
|
|
||||||
*/
|
|
||||||
std::string PlatformDetector::getCachePath(const std::string& appName) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
char path[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, path))) {
|
|
||||||
return std::string(path) + "\\" + appName + "\\cache";
|
|
||||||
}
|
|
||||||
return ".\\cache";
|
|
||||||
#elif defined(__SWITCH__)
|
|
||||||
return "sdmc:/cache/" + appName;
|
|
||||||
#elif defined(__linux__)
|
|
||||||
const char* cacheHome = getenv("XDG_CACHE_HOME");
|
|
||||||
if (cacheHome && cacheHome[0] != '\0') {
|
|
||||||
return std::string(cacheHome) + "/" + appName;
|
|
||||||
}
|
|
||||||
const char* home = getenv("HOME");
|
|
||||||
if (!home) {
|
|
||||||
struct passwd* pwd = getpwuid(getuid());
|
|
||||||
if (pwd) home = pwd->pw_dir;
|
|
||||||
}
|
|
||||||
if (home) {
|
|
||||||
return std::string(home) + "/.cache/" + appName;
|
|
||||||
}
|
|
||||||
return "./cache";
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
const char* home = getenv("HOME");
|
|
||||||
if (!home) {
|
|
||||||
struct passwd* pwd = getpwuid(getuid());
|
|
||||||
if (pwd) home = pwd->pw_dir;
|
|
||||||
}
|
|
||||||
if (home) {
|
|
||||||
return std::string(home) + "/Library/Caches/" + appName;
|
|
||||||
}
|
|
||||||
return "./cache";
|
|
||||||
#else
|
|
||||||
return "./cache";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台特定的日志路径
|
|
||||||
* @param appName 应用名称
|
|
||||||
* @return 日志文件目录路径
|
|
||||||
*/
|
|
||||||
std::string PlatformDetector::getLogPath(const std::string& appName) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
char path[MAX_PATH];
|
|
||||||
if (SUCCEEDED(SHGetFolderPathA(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, path))) {
|
|
||||||
return std::string(path) + "\\" + appName + "\\logs";
|
|
||||||
}
|
|
||||||
return ".\\logs";
|
|
||||||
#elif defined(__SWITCH__)
|
|
||||||
return "sdmc:/logs/" + appName;
|
|
||||||
#elif defined(__linux__)
|
|
||||||
const char* cacheHome = getenv("XDG_CACHE_HOME");
|
|
||||||
if (cacheHome && cacheHome[0] != '\0') {
|
|
||||||
return std::string(cacheHome) + "/" + appName + "/logs";
|
|
||||||
}
|
|
||||||
const char* home = getenv("HOME");
|
|
||||||
if (!home) {
|
|
||||||
struct passwd* pwd = getpwuid(getuid());
|
|
||||||
if (pwd) home = pwd->pw_dir;
|
|
||||||
}
|
|
||||||
if (home) {
|
|
||||||
return std::string(home) + "/.cache/" + appName + "/logs";
|
|
||||||
}
|
|
||||||
return "./logs";
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
const char* home = getenv("HOME");
|
|
||||||
if (!home) {
|
|
||||||
struct passwd* pwd = getpwuid(getuid());
|
|
||||||
if (pwd) home = pwd->pw_dir;
|
|
||||||
}
|
|
||||||
if (home) {
|
|
||||||
return std::string(home) + "/Library/Logs/" + appName;
|
|
||||||
}
|
|
||||||
return "./logs";
|
|
||||||
#else
|
|
||||||
return "./logs";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台特定的资源路径(Shader、纹理等)
|
|
||||||
* Switch平台使用romfs,其他平台使用相对路径
|
|
||||||
* @param appName 应用名称
|
|
||||||
* @return 资源目录路径
|
|
||||||
*/
|
|
||||||
std::string PlatformDetector::getResourcePath(const std::string& appName) {
|
|
||||||
#ifdef __SWITCH__
|
|
||||||
(void)appName;
|
|
||||||
return "romfs:/";
|
|
||||||
#else
|
|
||||||
(void)appName;
|
|
||||||
return "./resources/";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台特定的Shader路径
|
|
||||||
* @param appName 应用名称
|
|
||||||
* @return Shader目录路径
|
|
||||||
*/
|
|
||||||
std::string PlatformDetector::getShaderPath(const std::string& appName) {
|
|
||||||
#ifdef __SWITCH__
|
|
||||||
(void)appName;
|
|
||||||
return "romfs:/shaders/";
|
|
||||||
#else
|
|
||||||
(void)appName;
|
|
||||||
return "./shaders/";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台特定的Shader缓存路径
|
|
||||||
* Switch平台使用sdmc,其他平台使用系统缓存目录
|
|
||||||
* @param appName 应用名称
|
|
||||||
* @return Shader缓存目录路径
|
|
||||||
*/
|
|
||||||
std::string PlatformDetector::getShaderCachePath(const std::string& appName) {
|
|
||||||
#ifdef __SWITCH__
|
|
||||||
std::string name = appName.empty() ? "extra2d" : appName;
|
|
||||||
return "sdmc:/cache/" + name + "/shaders/";
|
|
||||||
#else
|
|
||||||
return getCachePath(appName.empty() ? "extra2d" : appName) + "/shaders/";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查平台是否使用romfs(只读文件系统)
|
|
||||||
* @return 使用romfs返回true
|
|
||||||
*/
|
|
||||||
bool PlatformDetector::usesRomfs() {
|
|
||||||
#ifdef __SWITCH__
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查平台是否支持热重载
|
|
||||||
* Switch平台不支持热重载(romfs只读)
|
|
||||||
* @return 支持热重载返回true
|
|
||||||
*/
|
|
||||||
bool PlatformDetector::supportsHotReload() {
|
|
||||||
#ifdef __SWITCH__
|
|
||||||
return false;
|
|
||||||
#else
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查平台是否为小端字节序
|
|
||||||
* @return 如果是小端字节序返回 true
|
|
||||||
*/
|
|
||||||
bool PlatformDetector::isLittleEndian() {
|
|
||||||
union {
|
|
||||||
uint32_t i;
|
|
||||||
char c[4];
|
|
||||||
} test = {0x01020304};
|
|
||||||
return test.c[0] == 0x04;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查平台是否为大端字节序
|
|
||||||
* @return 如果是大端字节序返回 true
|
|
||||||
*/
|
|
||||||
bool PlatformDetector::isBigEndian() {
|
|
||||||
return !isLittleEndian();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取平台信息摘要
|
|
||||||
* @return 平台信息字符串
|
|
||||||
*/
|
|
||||||
std::string PlatformDetector::getPlatformSummary() {
|
|
||||||
std::string summary;
|
|
||||||
summary += "Platform: ";
|
|
||||||
summary += platformName();
|
|
||||||
summary += "\n";
|
|
||||||
summary += "Memory: ";
|
|
||||||
summary += std::to_string(getSystemMemoryMB());
|
|
||||||
summary += " MB\n";
|
|
||||||
summary += "CPU Cores: ";
|
|
||||||
summary += std::to_string(getCPUCoreCount());
|
|
||||||
summary += "\n";
|
|
||||||
summary += "Endianness: ";
|
|
||||||
summary += isLittleEndian() ? "Little Endian" : "Big Endian";
|
|
||||||
summary += "\n";
|
|
||||||
summary += "Desktop Platform: ";
|
|
||||||
summary += isDesktopPlatform() ? "Yes" : "No";
|
|
||||||
summary += "\n";
|
|
||||||
summary += "Console Platform: ";
|
|
||||||
summary += isConsolePlatform() ? "Yes" : "No";
|
|
||||||
summary += "\n";
|
|
||||||
summary += "Recommended Resolution: ";
|
|
||||||
int width, height;
|
|
||||||
getRecommendedResolution(width, height);
|
|
||||||
summary += std::to_string(width);
|
|
||||||
summary += "x";
|
|
||||||
summary += std::to_string(height);
|
|
||||||
summary += "\n";
|
|
||||||
summary += "Default DPI: ";
|
|
||||||
summary += std::to_string(static_cast<int>(getDefaultDPI()));
|
|
||||||
return summary;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformCapabilities PlatformDetector::getWindowsCapabilities() {
|
|
||||||
PlatformCapabilities caps;
|
|
||||||
caps.supportsWindowed = true;
|
|
||||||
caps.supportsFullscreen = true;
|
|
||||||
caps.supportsBorderless = true;
|
|
||||||
caps.supportsCursor = true;
|
|
||||||
caps.supportsCursorHide = true;
|
|
||||||
caps.supportsDPIAwareness = true;
|
|
||||||
caps.supportsVSync = true;
|
|
||||||
caps.supportsMultiMonitor = true;
|
|
||||||
caps.supportsClipboard = true;
|
|
||||||
caps.supportsGamepad = true;
|
|
||||||
caps.supportsTouch = false;
|
|
||||||
caps.supportsKeyboard = true;
|
|
||||||
caps.supportsMouse = true;
|
|
||||||
caps.supportsResize = true;
|
|
||||||
caps.supportsHighDPI = true;
|
|
||||||
caps.maxTextureSize = 16384;
|
|
||||||
caps.preferredScreenWidth = 1920;
|
|
||||||
caps.preferredScreenHeight = 1080;
|
|
||||||
caps.defaultDPI = 96.0f;
|
|
||||||
return caps;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformCapabilities PlatformDetector::getLinuxCapabilities() {
|
|
||||||
PlatformCapabilities caps;
|
|
||||||
caps.supportsWindowed = true;
|
|
||||||
caps.supportsFullscreen = true;
|
|
||||||
caps.supportsBorderless = true;
|
|
||||||
caps.supportsCursor = true;
|
|
||||||
caps.supportsCursorHide = true;
|
|
||||||
caps.supportsDPIAwareness = true;
|
|
||||||
caps.supportsVSync = true;
|
|
||||||
caps.supportsMultiMonitor = true;
|
|
||||||
caps.supportsClipboard = true;
|
|
||||||
caps.supportsGamepad = true;
|
|
||||||
caps.supportsTouch = false;
|
|
||||||
caps.supportsKeyboard = true;
|
|
||||||
caps.supportsMouse = true;
|
|
||||||
caps.supportsResize = true;
|
|
||||||
caps.supportsHighDPI = true;
|
|
||||||
caps.maxTextureSize = 16384;
|
|
||||||
caps.preferredScreenWidth = 1920;
|
|
||||||
caps.preferredScreenHeight = 1080;
|
|
||||||
caps.defaultDPI = 96.0f;
|
|
||||||
return caps;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformCapabilities PlatformDetector::getMacOSCapabilities() {
|
|
||||||
PlatformCapabilities caps;
|
|
||||||
caps.supportsWindowed = true;
|
|
||||||
caps.supportsFullscreen = true;
|
|
||||||
caps.supportsBorderless = true;
|
|
||||||
caps.supportsCursor = true;
|
|
||||||
caps.supportsCursorHide = true;
|
|
||||||
caps.supportsDPIAwareness = true;
|
|
||||||
caps.supportsVSync = true;
|
|
||||||
caps.supportsMultiMonitor = true;
|
|
||||||
caps.supportsClipboard = true;
|
|
||||||
caps.supportsGamepad = true;
|
|
||||||
caps.supportsTouch = false;
|
|
||||||
caps.supportsKeyboard = true;
|
|
||||||
caps.supportsMouse = true;
|
|
||||||
caps.supportsResize = true;
|
|
||||||
caps.supportsHighDPI = true;
|
|
||||||
caps.maxTextureSize = 16384;
|
|
||||||
caps.preferredScreenWidth = 1920;
|
|
||||||
caps.preferredScreenHeight = 1080;
|
|
||||||
caps.defaultDPI = 144.0f;
|
|
||||||
return caps;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformCapabilities PlatformDetector::getSwitchCapabilities() {
|
|
||||||
PlatformCapabilities caps;
|
|
||||||
caps.supportsWindowed = false;
|
|
||||||
caps.supportsFullscreen = true;
|
|
||||||
caps.supportsBorderless = false;
|
|
||||||
caps.supportsCursor = false;
|
|
||||||
caps.supportsCursorHide = false;
|
|
||||||
caps.supportsDPIAwareness = false;
|
|
||||||
caps.supportsVSync = true;
|
|
||||||
caps.supportsMultiMonitor = false;
|
|
||||||
caps.supportsClipboard = false;
|
|
||||||
caps.supportsGamepad = true;
|
|
||||||
caps.supportsTouch = true;
|
|
||||||
caps.supportsKeyboard = false;
|
|
||||||
caps.supportsMouse = false;
|
|
||||||
caps.supportsResize = false;
|
|
||||||
caps.supportsHighDPI = false;
|
|
||||||
caps.maxTextureSize = 8192;
|
|
||||||
caps.preferredScreenWidth = 1920;
|
|
||||||
caps.preferredScreenHeight = 1080;
|
|
||||||
caps.defaultDPI = 96.0f;
|
|
||||||
return caps;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppConfig PlatformDetector::getWindowsDefaults() {
|
|
||||||
AppConfig config = AppConfig::createDefault();
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppConfig PlatformDetector::getLinuxDefaults() {
|
|
||||||
AppConfig config = AppConfig::createDefault();
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppConfig PlatformDetector::getMacOSDefaults() {
|
|
||||||
AppConfig config = AppConfig::createDefault();
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppConfig PlatformDetector::getSwitchDefaults() {
|
|
||||||
AppConfig config = AppConfig::createDefault();
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,242 @@
|
||||||
|
#include <extra2d/core/module.h>
|
||||||
|
#include <extra2d/core/module_macros.h>
|
||||||
|
#include <extra2d/core/module_meta.h>
|
||||||
|
#include <extra2d/utils/logger.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
ModuleRegistry &ModuleRegistry::instance() {
|
||||||
|
static ModuleRegistry instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleRegistry::registerMeta(ModuleMetaBase *meta) {
|
||||||
|
if (!meta)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const auto *existing : metas_) {
|
||||||
|
if (existing && std::string(existing->getName()) == meta->getName()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
metas_.push_back(meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ModuleMetaBase *> ModuleRegistry::getAllMetas() const {
|
||||||
|
return metas_;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleMetaBase *ModuleRegistry::getMeta(const char *name) const {
|
||||||
|
if (!name)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
for (auto *meta : metas_) {
|
||||||
|
if (meta && std::string(meta->getName()) == name) {
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Module *ModuleRegistry::getModule(const char *name) const {
|
||||||
|
auto it = instanceMap_.find(name ? name : "");
|
||||||
|
return it != instanceMap_.end() ? it->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModuleRegistry::hasModule(const char *name) const {
|
||||||
|
return instanceMap_.find(name ? name : "") != instanceMap_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Module *> ModuleRegistry::getAllModules() const {
|
||||||
|
std::vector<Module *> result;
|
||||||
|
result.reserve(instances_.size());
|
||||||
|
for (const auto &inst : instances_) {
|
||||||
|
result.push_back(inst.get());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModuleRegistry::hasCircularDependency() const {
|
||||||
|
std::unordered_map<std::string, std::unordered_set<std::string>> graph;
|
||||||
|
|
||||||
|
for (auto *meta : metas_) {
|
||||||
|
if (!meta)
|
||||||
|
continue;
|
||||||
|
std::string name = meta->getName();
|
||||||
|
for (const auto *dep : meta->getDependencies()) {
|
||||||
|
graph[name].insert(dep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<std::string> visited;
|
||||||
|
std::unordered_set<std::string> recStack;
|
||||||
|
|
||||||
|
std::function<bool(const std::string &)> hasCycle =
|
||||||
|
[&](const std::string &node) -> bool {
|
||||||
|
visited.insert(node);
|
||||||
|
recStack.insert(node);
|
||||||
|
|
||||||
|
for (const auto &neighbor : graph[node]) {
|
||||||
|
if (visited.find(neighbor) == visited.end()) {
|
||||||
|
if (hasCycle(neighbor))
|
||||||
|
return true;
|
||||||
|
} else if (recStack.find(neighbor) != recStack.end()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recStack.erase(node);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto *meta : metas_) {
|
||||||
|
if (!meta)
|
||||||
|
continue;
|
||||||
|
std::string name = meta->getName();
|
||||||
|
if (visited.find(name) == visited.end()) {
|
||||||
|
if (hasCycle(name))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ModuleMetaBase *> ModuleRegistry::sortByDependency() {
|
||||||
|
if (hasCircularDependency()) {
|
||||||
|
E2D_LOG_ERROR("Circular dependency detected in modules!");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::string, int> inDegree;
|
||||||
|
std::unordered_map<std::string, std::vector<std::string>> graph;
|
||||||
|
std::unordered_map<std::string, ModuleMetaBase *> nameToMeta;
|
||||||
|
|
||||||
|
for (auto *meta : metas_) {
|
||||||
|
if (!meta)
|
||||||
|
continue;
|
||||||
|
std::string name = meta->getName();
|
||||||
|
nameToMeta[name] = meta;
|
||||||
|
if (inDegree.find(name) == inDegree.end()) {
|
||||||
|
inDegree[name] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto *dep : meta->getDependencies()) {
|
||||||
|
std::string depName = dep;
|
||||||
|
graph[depName].push_back(name);
|
||||||
|
inDegree[name]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ModuleMetaBase *> sorted;
|
||||||
|
std::vector<std::string> currentLevel;
|
||||||
|
|
||||||
|
for (const auto &[name, degree] : inDegree) {
|
||||||
|
if (degree == 0) {
|
||||||
|
currentLevel.push_back(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!currentLevel.empty()) {
|
||||||
|
std::sort(currentLevel.begin(), currentLevel.end(),
|
||||||
|
[&nameToMeta](const std::string &a, const std::string &b) {
|
||||||
|
auto *metaA = nameToMeta[a];
|
||||||
|
auto *metaB = nameToMeta[b];
|
||||||
|
if (!metaA || !metaB)
|
||||||
|
return a < b;
|
||||||
|
return metaA->getPriority() < metaB->getPriority();
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<std::string> nextLevel;
|
||||||
|
|
||||||
|
for (const auto &name : currentLevel) {
|
||||||
|
if (nameToMeta.find(name) != nameToMeta.end()) {
|
||||||
|
sorted.push_back(nameToMeta[name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &neighbor : graph[name]) {
|
||||||
|
inDegree[neighbor]--;
|
||||||
|
if (inDegree[neighbor] == 0) {
|
||||||
|
nextLevel.push_back(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentLevel = std::move(nextLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModuleRegistry::createAndInitAll() {
|
||||||
|
if (initialized_) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态库中模块已通过 E2D_MODULE 宏自动注册
|
||||||
|
// 无需手动调用 registerCoreModules()
|
||||||
|
|
||||||
|
E2D_LOG_INFO("ModuleRegistry: {} modules registered", metas_.size());
|
||||||
|
for (auto *meta : metas_) {
|
||||||
|
if (meta) {
|
||||||
|
E2D_LOG_INFO(" - {} (priority: {})", meta->getName(),
|
||||||
|
meta->getPriority());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sorted = sortByDependency();
|
||||||
|
if (sorted.empty() && !metas_.empty()) {
|
||||||
|
E2D_LOG_ERROR(
|
||||||
|
"Failed to sort modules by dependency - possible circular dependency");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto *meta : sorted) {
|
||||||
|
if (!meta)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Module *instance = meta->create();
|
||||||
|
if (!instance) {
|
||||||
|
E2D_LOG_ERROR("Failed to create module: {}", meta->getName());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyBinderImpl binder;
|
||||||
|
meta->bindProperties(instance, binder);
|
||||||
|
|
||||||
|
instance->setupModule();
|
||||||
|
|
||||||
|
instances_.emplace_back(instance);
|
||||||
|
instanceMap_[meta->getName()] = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleRegistry::destroyAll() {
|
||||||
|
if (!initialized_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
E2D_LOG_INFO("Destroying {} modules in reverse order", instances_.size());
|
||||||
|
|
||||||
|
// 先销毁所有模块(逆序)
|
||||||
|
for (auto it = instances_.rbegin(); it != instances_.rend(); ++it) {
|
||||||
|
if (*it) {
|
||||||
|
E2D_LOG_INFO("Destroying module: {}", (*it)->getName());
|
||||||
|
(*it)->destroyModule();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 然后清理实例
|
||||||
|
instances_.clear();
|
||||||
|
instanceMap_.clear();
|
||||||
|
initialized_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -633,7 +633,7 @@ void GLRenderer::resetStats() { stats_ = Stats{}; }
|
||||||
*/
|
*/
|
||||||
void GLRenderer::initShapeRendering() {
|
void GLRenderer::initShapeRendering() {
|
||||||
// 从ShaderManager获取形状着色器
|
// 从ShaderManager获取形状着色器
|
||||||
shapeShader_ = ShaderManager::getInstance().getBuiltin("builtin_shape");
|
shapeShader_ = ShaderManager::getInstance().getBuiltin("shape");
|
||||||
if (!shapeShader_) {
|
if (!shapeShader_) {
|
||||||
E2D_LOG_WARN("Failed to get builtin shape shader, loading from manager");
|
E2D_LOG_WARN("Failed to get builtin shape shader, loading from manager");
|
||||||
if (!ShaderManager::getInstance().isInitialized()) {
|
if (!ShaderManager::getInstance().isInitialized()) {
|
||||||
|
|
|
||||||
|
|
@ -3,42 +3,30 @@
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取单例实例
|
|
||||||
* @return Shader管理器实例引用
|
|
||||||
*/
|
|
||||||
ShaderManager& ShaderManager::getInstance() {
|
ShaderManager& ShaderManager::getInstance() {
|
||||||
static ShaderManager instance;
|
static ShaderManager instance;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 使用平台默认路径初始化Shader系统
|
|
||||||
* 自动检测平台并使用正确的路径(romfs/sdmc/相对路径)
|
|
||||||
* @param factory 渲染后端Shader工厂
|
|
||||||
* @param appName 应用名称(用于缓存目录)
|
|
||||||
* @return 初始化成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool ShaderManager::init(Ptr<IShaderFactory> factory, const std::string& appName) {
|
bool ShaderManager::init(Ptr<IShaderFactory> factory, const std::string& appName) {
|
||||||
std::string shaderDir = PlatformDetector::getShaderPath(appName);
|
std::string shaderDir;
|
||||||
std::string cacheDir = PlatformDetector::getShaderCachePath(appName);
|
std::string cacheDir;
|
||||||
|
|
||||||
hotReloadSupported_ = PlatformDetector::supportsHotReload();
|
#ifdef __SWITCH__
|
||||||
|
shaderDir = "romfs:/shaders/";
|
||||||
E2D_LOG_INFO("Platform: {} (HotReload: {})",
|
cacheDir = "sdmc:/config/" + appName + "/shader_cache/";
|
||||||
PlatformDetector::platformName(),
|
hotReloadSupported_ = false;
|
||||||
hotReloadSupported_ ? "supported" : "not supported");
|
E2D_LOG_INFO("Platform: Switch (HotReload: not supported)");
|
||||||
|
#else
|
||||||
|
shaderDir = "shaders/";
|
||||||
|
cacheDir = "shader_cache/";
|
||||||
|
hotReloadSupported_ = true;
|
||||||
|
E2D_LOG_INFO("Platform: Desktop (HotReload: supported)");
|
||||||
|
#endif
|
||||||
|
|
||||||
return init(shaderDir, cacheDir, factory);
|
return init(shaderDir, cacheDir, factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 初始化Shader系统
|
|
||||||
* @param shaderDir Shader文件目录
|
|
||||||
* @param cacheDir 缓存目录
|
|
||||||
* @param factory 渲染后端Shader工厂
|
|
||||||
* @return 初始化成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool ShaderManager::init(const std::string& shaderDir,
|
bool ShaderManager::init(const std::string& shaderDir,
|
||||||
const std::string& cacheDir,
|
const std::string& cacheDir,
|
||||||
Ptr<IShaderFactory> factory) {
|
Ptr<IShaderFactory> factory) {
|
||||||
|
|
@ -56,13 +44,13 @@ bool ShaderManager::init(const std::string& shaderDir,
|
||||||
cacheDir_ = cacheDir;
|
cacheDir_ = cacheDir;
|
||||||
factory_ = factory;
|
factory_ = factory;
|
||||||
|
|
||||||
hotReloadSupported_ = PlatformDetector::supportsHotReload();
|
|
||||||
|
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
|
hotReloadSupported_ = false;
|
||||||
if (!ShaderCache::getInstance().init(cacheDir_)) {
|
if (!ShaderCache::getInstance().init(cacheDir_)) {
|
||||||
E2D_LOG_WARN("Failed to initialize shader cache on Switch");
|
E2D_LOG_WARN("Failed to initialize shader cache on Switch");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
hotReloadSupported_ = true;
|
||||||
if (!ShaderCache::getInstance().init(cacheDir_)) {
|
if (!ShaderCache::getInstance().init(cacheDir_)) {
|
||||||
E2D_LOG_WARN("Failed to initialize shader cache, caching disabled");
|
E2D_LOG_WARN("Failed to initialize shader cache, caching disabled");
|
||||||
}
|
}
|
||||||
|
|
@ -78,16 +66,10 @@ bool ShaderManager::init(const std::string& shaderDir,
|
||||||
|
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
E2D_LOG_INFO("ShaderManager initialized");
|
E2D_LOG_INFO("ShaderManager initialized");
|
||||||
E2D_LOG_INFO(" Shader directory: {}", shaderDir_);
|
|
||||||
E2D_LOG_INFO(" Cache directory: {}", cacheDir_);
|
|
||||||
E2D_LOG_INFO(" Hot reload: {}", hotReloadSupported_ ? "supported" : "not supported");
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 关闭Shader系统
|
|
||||||
*/
|
|
||||||
void ShaderManager::shutdown() {
|
void ShaderManager::shutdown() {
|
||||||
if (!initialized_) {
|
if (!initialized_) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -105,175 +87,57 @@ void ShaderManager::shutdown() {
|
||||||
E2D_LOG_INFO("ShaderManager shutdown");
|
E2D_LOG_INFO("ShaderManager shutdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从分离文件加载Shader
|
|
||||||
* @param name Shader名称
|
|
||||||
* @param vertPath 顶点着色器文件路径
|
|
||||||
* @param fragPath 片段着色器文件路径
|
|
||||||
* @return 加载的Shader实例
|
|
||||||
*/
|
|
||||||
Ptr<IShader> ShaderManager::loadFromFiles(const std::string& name,
|
Ptr<IShader> ShaderManager::loadFromFiles(const std::string& name,
|
||||||
const std::string& vertPath,
|
const std::string& vertPath,
|
||||||
const std::string& fragPath) {
|
const std::string& fragPath) {
|
||||||
if (!initialized_) {
|
if (!factory_) {
|
||||||
E2D_LOG_ERROR("ShaderManager not initialized");
|
E2D_LOG_ERROR("Shader factory not initialized");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = shaders_.find(name);
|
auto vertSource = loader_.readFile(vertPath);
|
||||||
if (it != shaders_.end()) {
|
auto fragSource = loader_.readFile(fragPath);
|
||||||
return it->second.shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderLoadResult result = loader_.loadFromSeparateFiles(name, vertPath, fragPath);
|
if (vertSource.empty() || fragSource.empty()) {
|
||||||
if (!result.success) {
|
E2D_LOG_ERROR("Failed to load shader sources: {} / {}", vertPath, fragPath);
|
||||||
E2D_LOG_ERROR("Failed to load shader files: {} - {}", vertPath, fragPath);
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string sourceHash = ShaderCache::computeHash(result.vertSource, result.fragSource);
|
return loadFromSource(name, vertSource, fragSource);
|
||||||
Ptr<IShader> shader = loadFromCache(name, sourceHash, result.vertSource, result.fragSource);
|
|
||||||
|
|
||||||
if (!shader) {
|
|
||||||
E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}", name);
|
|
||||||
shader = factory_->createFromSource(name, result.vertSource, result.fragSource);
|
|
||||||
if (!shader) {
|
|
||||||
E2D_LOG_ERROR("Failed to create shader from source: {}", name);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> binary;
|
|
||||||
if (factory_->getShaderBinary(*shader, binary)) {
|
|
||||||
E2D_LOG_DEBUG("Got shader binary, size: {} bytes", binary.size());
|
|
||||||
ShaderCacheEntry entry;
|
|
||||||
entry.name = name;
|
|
||||||
entry.sourceHash = sourceHash;
|
|
||||||
entry.binary = binary;
|
|
||||||
entry.dependencies = result.dependencies;
|
|
||||||
ShaderCache::getInstance().saveCache(entry);
|
|
||||||
} else {
|
|
||||||
E2D_LOG_WARN("Failed to get shader binary for: {}", name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderInfo info;
|
|
||||||
info.shader = shader;
|
|
||||||
info.vertSource = result.vertSource;
|
|
||||||
info.fragSource = result.fragSource;
|
|
||||||
info.filePaths = {vertPath, fragPath};
|
|
||||||
info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(), result.dependencies.end());
|
|
||||||
|
|
||||||
info.metadata.name = name;
|
|
||||||
info.metadata.vertPath = vertPath;
|
|
||||||
info.metadata.fragPath = fragPath;
|
|
||||||
|
|
||||||
shaders_[name] = std::move(info);
|
|
||||||
|
|
||||||
if (hotReloadEnabled_ && hotReloadSupported_) {
|
|
||||||
auto callback = [this, name](const FileChangeEvent& event) {
|
|
||||||
this->handleFileChange(name, event);
|
|
||||||
};
|
|
||||||
ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
E2D_LOG_DEBUG("Shader loaded: {}", name);
|
|
||||||
return shader;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从组合文件加载Shader
|
|
||||||
* @param path 组合Shader文件路径
|
|
||||||
* @return 加载的Shader实例
|
|
||||||
*/
|
|
||||||
Ptr<IShader> ShaderManager::loadFromCombinedFile(const std::string& path) {
|
Ptr<IShader> ShaderManager::loadFromCombinedFile(const std::string& path) {
|
||||||
if (!initialized_) {
|
if (!factory_) {
|
||||||
E2D_LOG_ERROR("ShaderManager not initialized");
|
E2D_LOG_ERROR("Shader factory not initialized");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderMetadata metadata = loader_.getMetadata(path);
|
auto result = loader_.loadFromCombinedFile(path);
|
||||||
std::string name = metadata.name.empty() ? path : metadata.name;
|
|
||||||
|
|
||||||
auto it = shaders_.find(name);
|
|
||||||
if (it != shaders_.end()) {
|
|
||||||
return it->second.shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderLoadResult result = loader_.loadFromCombinedFile(path);
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
E2D_LOG_ERROR("Failed to load combined shader file: {}", path);
|
E2D_LOG_ERROR("Failed to load combined shader: {}", path);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string sourceHash = ShaderCache::computeHash(result.vertSource, result.fragSource);
|
std::string name = path.substr(path.find_last_of("/\\") + 1);
|
||||||
Ptr<IShader> shader = loadFromCache(name, sourceHash, result.vertSource, result.fragSource);
|
return loadFromSource(name, result.vertSource, result.fragSource);
|
||||||
|
|
||||||
if (!shader) {
|
|
||||||
E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}", name);
|
|
||||||
shader = factory_->createFromSource(name, result.vertSource, result.fragSource);
|
|
||||||
if (!shader) {
|
|
||||||
E2D_LOG_ERROR("Failed to create shader from source: {}", name);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> binary;
|
|
||||||
if (factory_->getShaderBinary(*shader, binary)) {
|
|
||||||
E2D_LOG_DEBUG("Got shader binary, size: {} bytes", binary.size());
|
|
||||||
ShaderCacheEntry entry;
|
|
||||||
entry.name = name;
|
|
||||||
entry.sourceHash = sourceHash;
|
|
||||||
entry.binary = binary;
|
|
||||||
entry.dependencies = result.dependencies;
|
|
||||||
ShaderCache::getInstance().saveCache(entry);
|
|
||||||
} else {
|
|
||||||
E2D_LOG_WARN("Failed to get shader binary for: {}", name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderInfo info;
|
|
||||||
info.shader = shader;
|
|
||||||
info.vertSource = result.vertSource;
|
|
||||||
info.fragSource = result.fragSource;
|
|
||||||
info.filePaths = {path};
|
|
||||||
info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(), result.dependencies.end());
|
|
||||||
info.metadata = metadata;
|
|
||||||
|
|
||||||
shaders_[name] = std::move(info);
|
|
||||||
|
|
||||||
if (hotReloadEnabled_ && hotReloadSupported_) {
|
|
||||||
auto callback = [this, name](const FileChangeEvent& event) {
|
|
||||||
this->handleFileChange(name, event);
|
|
||||||
};
|
|
||||||
ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
E2D_LOG_DEBUG("Shader loaded from combined file: {}", name);
|
|
||||||
return shader;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从源码加载Shader
|
|
||||||
* @param name Shader名称
|
|
||||||
* @param vertSource 顶点着色器源码
|
|
||||||
* @param fragSource 片段着色器源码
|
|
||||||
* @return 加载的Shader实例
|
|
||||||
*/
|
|
||||||
Ptr<IShader> ShaderManager::loadFromSource(const std::string& name,
|
Ptr<IShader> ShaderManager::loadFromSource(const std::string& name,
|
||||||
const std::string& vertSource,
|
const std::string& vertSource,
|
||||||
const std::string& fragSource) {
|
const std::string& fragSource) {
|
||||||
if (!initialized_) {
|
if (!factory_) {
|
||||||
E2D_LOG_ERROR("ShaderManager not initialized");
|
E2D_LOG_ERROR("Shader factory not initialized");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = shaders_.find(name);
|
if (has(name)) {
|
||||||
if (it != shaders_.end()) {
|
E2D_LOG_WARN("Shader already exists: {}", name);
|
||||||
return it->second.shader;
|
return get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ptr<IShader> shader = factory_->createFromSource(name, vertSource, fragSource);
|
auto shader = factory_->createFromSource(name, vertSource, fragSource);
|
||||||
if (!shader) {
|
if (!shader) {
|
||||||
E2D_LOG_ERROR("Failed to create shader from source: {}", name);
|
E2D_LOG_ERROR("Failed to create shader: {}", name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -281,19 +145,13 @@ Ptr<IShader> ShaderManager::loadFromSource(const std::string& name,
|
||||||
info.shader = shader;
|
info.shader = shader;
|
||||||
info.vertSource = vertSource;
|
info.vertSource = vertSource;
|
||||||
info.fragSource = fragSource;
|
info.fragSource = fragSource;
|
||||||
info.metadata.name = name;
|
|
||||||
|
|
||||||
shaders_[name] = std::move(info);
|
shaders_[name] = std::move(info);
|
||||||
|
|
||||||
E2D_LOG_DEBUG("Shader loaded from source: {}", name);
|
E2D_LOG_INFO("Shader loaded: {}", name);
|
||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取已加载的Shader
|
|
||||||
* @param name Shader名称
|
|
||||||
* @return Shader实例,不存在返回nullptr
|
|
||||||
*/
|
|
||||||
Ptr<IShader> ShaderManager::get(const std::string& name) const {
|
Ptr<IShader> ShaderManager::get(const std::string& name) const {
|
||||||
auto it = shaders_.find(name);
|
auto it = shaders_.find(name);
|
||||||
if (it != shaders_.end()) {
|
if (it != shaders_.end()) {
|
||||||
|
|
@ -302,232 +160,118 @@ Ptr<IShader> ShaderManager::get(const std::string& name) const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查Shader是否存在
|
|
||||||
* @param name Shader名称
|
|
||||||
* @return 存在返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool ShaderManager::has(const std::string& name) const {
|
bool ShaderManager::has(const std::string& name) const {
|
||||||
return shaders_.find(name) != shaders_.end();
|
return shaders_.find(name) != shaders_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 移除Shader
|
|
||||||
* @param name Shader名称
|
|
||||||
*/
|
|
||||||
void ShaderManager::remove(const std::string& name) {
|
void ShaderManager::remove(const std::string& name) {
|
||||||
auto it = shaders_.find(name);
|
auto it = shaders_.find(name);
|
||||||
if (it != shaders_.end()) {
|
if (it != shaders_.end()) {
|
||||||
ShaderHotReloader::getInstance().unwatch(name);
|
|
||||||
shaders_.erase(it);
|
shaders_.erase(it);
|
||||||
E2D_LOG_DEBUG("Shader removed: {}", name);
|
E2D_LOG_INFO("Shader removed: {}", name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 清除所有Shader
|
|
||||||
*/
|
|
||||||
void ShaderManager::clear() {
|
void ShaderManager::clear() {
|
||||||
if (hotReloadSupported_) {
|
|
||||||
for (const auto& pair : shaders_) {
|
|
||||||
ShaderHotReloader::getInstance().unwatch(pair.first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
shaders_.clear();
|
shaders_.clear();
|
||||||
E2D_LOG_DEBUG("All shaders cleared");
|
E2D_LOG_INFO("All shaders cleared");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 注册重载回调
|
|
||||||
* @param name Shader名称
|
|
||||||
* @param callback 重载回调函数
|
|
||||||
*/
|
|
||||||
void ShaderManager::setReloadCallback(const std::string& name, ShaderReloadCallback callback) {
|
void ShaderManager::setReloadCallback(const std::string& name, ShaderReloadCallback callback) {
|
||||||
auto it = shaders_.find(name);
|
auto it = shaders_.find(name);
|
||||||
if (it != shaders_.end()) {
|
if (it != shaders_.end()) {
|
||||||
it->second.reloadCallback = callback;
|
it->second.reloadCallback = std::move(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 启用/禁用热重载
|
|
||||||
* @param enabled 是否启用
|
|
||||||
*/
|
|
||||||
void ShaderManager::setHotReloadEnabled(bool enabled) {
|
void ShaderManager::setHotReloadEnabled(bool enabled) {
|
||||||
if (!hotReloadSupported_) {
|
if (!hotReloadSupported_ && enabled) {
|
||||||
E2D_LOG_WARN("Hot reload not supported on this platform");
|
E2D_LOG_WARN("Hot reload not supported on this platform");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
hotReloadEnabled_ = enabled;
|
hotReloadEnabled_ = enabled;
|
||||||
ShaderHotReloader::getInstance().setEnabled(enabled);
|
|
||||||
E2D_LOG_INFO("Hot reload {}", enabled ? "enabled" : "disabled");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 检查热重载是否启用
|
|
||||||
* @return 启用返回true,否则返回false
|
|
||||||
*/
|
|
||||||
bool ShaderManager::isHotReloadEnabled() const {
|
bool ShaderManager::isHotReloadEnabled() const {
|
||||||
return hotReloadEnabled_ && hotReloadSupported_;
|
return hotReloadEnabled_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 更新热重载系统(主循环调用)
|
|
||||||
*/
|
|
||||||
void ShaderManager::update() {
|
void ShaderManager::update() {
|
||||||
if (hotReloadEnabled_ && hotReloadSupported_) {
|
if (!hotReloadEnabled_ || !hotReloadSupported_) {
|
||||||
ShaderHotReloader::getInstance().update();
|
return;
|
||||||
}
|
}
|
||||||
|
ShaderHotReloader::getInstance().update();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 手动重载Shader
|
|
||||||
* @param name Shader名称
|
|
||||||
* @return 重载成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool ShaderManager::reload(const std::string& name) {
|
bool ShaderManager::reload(const std::string& name) {
|
||||||
auto it = shaders_.find(name);
|
auto it = shaders_.find(name);
|
||||||
if (it == shaders_.end()) {
|
if (it == shaders_.end()) {
|
||||||
E2D_LOG_WARN("Shader not found for reload: {}", name);
|
E2D_LOG_ERROR("Shader not found: {}", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderInfo& info = it->second;
|
auto shader = factory_->createFromSource(name, it->second.vertSource, it->second.fragSource);
|
||||||
|
if (!shader) {
|
||||||
std::string vertSource = info.vertSource;
|
|
||||||
std::string fragSource = info.fragSource;
|
|
||||||
|
|
||||||
if (!info.metadata.vertPath.empty() && !info.metadata.fragPath.empty()) {
|
|
||||||
ShaderLoadResult result = loader_.loadFromSeparateFiles(name, info.metadata.vertPath, info.metadata.fragPath);
|
|
||||||
if (result.success) {
|
|
||||||
vertSource = result.vertSource;
|
|
||||||
fragSource = result.fragSource;
|
|
||||||
}
|
|
||||||
} else if (!info.metadata.combinedPath.empty()) {
|
|
||||||
ShaderLoadResult result = loader_.loadFromCombinedFile(info.metadata.combinedPath);
|
|
||||||
if (result.success) {
|
|
||||||
vertSource = result.vertSource;
|
|
||||||
fragSource = result.fragSource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ptr<IShader> newShader = factory_->createFromSource(name, vertSource, fragSource);
|
|
||||||
if (!newShader) {
|
|
||||||
E2D_LOG_ERROR("Failed to reload shader: {}", name);
|
E2D_LOG_ERROR("Failed to reload shader: {}", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
info.shader = newShader;
|
it->second.shader = shader;
|
||||||
info.vertSource = vertSource;
|
|
||||||
info.fragSource = fragSource;
|
|
||||||
|
|
||||||
if (info.reloadCallback) {
|
if (it->second.reloadCallback) {
|
||||||
info.reloadCallback(newShader);
|
it->second.reloadCallback(shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
E2D_LOG_INFO("Shader reloaded: {}", name);
|
E2D_LOG_INFO("Shader reloaded: {}", name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取内置Shader
|
|
||||||
* @param name 内置Shader名称
|
|
||||||
* @return Shader实例
|
|
||||||
*/
|
|
||||||
Ptr<IShader> ShaderManager::getBuiltin(const std::string& name) {
|
Ptr<IShader> ShaderManager::getBuiltin(const std::string& name) {
|
||||||
Ptr<IShader> shader = get(name);
|
return get("builtin_" + name);
|
||||||
if (shader) {
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string path = shaderDir_ + "builtin/" + name + ".shader";
|
|
||||||
return loadFromCombinedFile(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 加载所有内置Shader
|
|
||||||
* @return 加载成功返回true,失败返回false
|
|
||||||
*/
|
|
||||||
bool ShaderManager::loadBuiltinShaders() {
|
bool ShaderManager::loadBuiltinShaders() {
|
||||||
if (!initialized_) {
|
E2D_LOG_INFO("Loading builtin shaders...");
|
||||||
E2D_LOG_ERROR("ShaderManager not initialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool allSuccess = true;
|
const std::vector<std::string> builtinShaders = {
|
||||||
|
|
||||||
const char* builtinNames[] = {
|
|
||||||
"sprite",
|
"sprite",
|
||||||
"particle",
|
|
||||||
"shape",
|
"shape",
|
||||||
"postprocess",
|
"font",
|
||||||
"font"
|
"particle",
|
||||||
|
"postprocess"
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const char* name : builtinNames) {
|
int loaded = 0;
|
||||||
|
for (const auto& name : builtinShaders) {
|
||||||
std::string path = shaderDir_ + "builtin/" + name + ".shader";
|
std::string path = shaderDir_ + "builtin/" + name + ".shader";
|
||||||
std::string shaderName = std::string("builtin_") + name;
|
if (ShaderLoader::fileExists(path)) {
|
||||||
|
auto result = loader_.loadFromCombinedFile(path);
|
||||||
if (!loadFromCombinedFile(path)) {
|
if (result.success) {
|
||||||
E2D_LOG_ERROR("Failed to load builtin {} shader from: {}", name, path);
|
auto shader = factory_->createFromSource("builtin_" + name, result.vertSource, result.fragSource);
|
||||||
allSuccess = false;
|
|
||||||
} else {
|
|
||||||
auto it = shaders_.find(name);
|
|
||||||
if (it != shaders_.end()) {
|
|
||||||
shaders_[shaderName] = it->second;
|
|
||||||
shaders_.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allSuccess) {
|
|
||||||
E2D_LOG_INFO("All builtin shaders loaded");
|
|
||||||
}
|
|
||||||
|
|
||||||
return allSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从缓存加载Shader
|
|
||||||
* @param name Shader名称
|
|
||||||
* @param sourceHash 源码哈希值
|
|
||||||
* @param vertSource 顶点着色器源码
|
|
||||||
* @param fragSource 片段着色器源码
|
|
||||||
* @return Shader实例
|
|
||||||
*/
|
|
||||||
Ptr<IShader> ShaderManager::loadFromCache(const std::string& name,
|
|
||||||
const std::string& sourceHash,
|
|
||||||
const std::string& vertSource,
|
|
||||||
const std::string& fragSource) {
|
|
||||||
if (!ShaderCache::getInstance().isInitialized()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ShaderCache::getInstance().hasValidCache(name, sourceHash)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ptr<ShaderCacheEntry> entry = ShaderCache::getInstance().loadCache(name);
|
|
||||||
if (!entry || entry->binary.empty()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ptr<IShader> shader = factory_->createFromBinary(name, entry->binary);
|
|
||||||
if (shader) {
|
if (shader) {
|
||||||
E2D_LOG_DEBUG("Shader loaded from cache: {}", name);
|
ShaderInfo info;
|
||||||
|
info.shader = shader;
|
||||||
|
info.vertSource = result.vertSource;
|
||||||
|
info.fragSource = result.fragSource;
|
||||||
|
shaders_["builtin_" + name] = std::move(info);
|
||||||
|
loaded++;
|
||||||
|
E2D_LOG_DEBUG("Loaded builtin shader: {}", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return shader;
|
E2D_LOG_INFO("Builtin shaders loaded: {}/{}", loaded, builtinShaders.size());
|
||||||
|
return loaded > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderManager::createBuiltinShaderSources() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 处理文件变化事件
|
|
||||||
* @param shaderName Shader名称
|
|
||||||
* @param event 文件变化事件
|
|
||||||
*/
|
|
||||||
void ShaderManager::handleFileChange(const std::string& shaderName, const FileChangeEvent& event) {
|
void ShaderManager::handleFileChange(const std::string& shaderName, const FileChangeEvent& event) {
|
||||||
E2D_LOG_DEBUG("Shader file changed: {} -> {}", shaderName, event.filepath);
|
if (hotReloadEnabled_) {
|
||||||
reload(shaderName);
|
reload(shaderName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include <extra2d/modules/config_module.h>
|
#include <extra2d/modules/config_module.h>
|
||||||
|
#include <extra2d/core/module_macros.h>
|
||||||
#include <extra2d/utils/logger.h>
|
#include <extra2d/utils/logger.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -51,3 +52,5 @@ void ConfigModule::destroyModule() {
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
||||||
|
E2D_MODULE(ConfigModule, 0)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include <extra2d/modules/input_module.h>
|
#include <extra2d/modules/input_module.h>
|
||||||
|
#include <extra2d/core/module_macros.h>
|
||||||
#include <extra2d/core/service_locator.h>
|
#include <extra2d/core/service_locator.h>
|
||||||
#include <extra2d/services/event_service.h>
|
#include <extra2d/services/event_service.h>
|
||||||
#include <extra2d/utils/logger.h>
|
#include <extra2d/utils/logger.h>
|
||||||
|
|
@ -24,8 +25,18 @@ void InputModule::setupModule() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果 window 还没设置,延迟初始化
|
||||||
|
if (!window_) {
|
||||||
|
E2D_LOG_INFO("Input module waiting for window");
|
||||||
|
setInitialized(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeWithWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputModule::initializeWithWindow() {
|
||||||
if (!window_) {
|
if (!window_) {
|
||||||
E2D_LOG_ERROR("Window not set - cannot initialize input module");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,13 +60,24 @@ void InputModule::setupModule() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setInitialized(true);
|
|
||||||
E2D_LOG_INFO("Input module initialized");
|
E2D_LOG_INFO("Input module initialized");
|
||||||
E2D_LOG_INFO(" Deadzone: {}", config_.deadzone);
|
E2D_LOG_INFO(" Deadzone: {}", config_.deadzone);
|
||||||
E2D_LOG_INFO(" Mouse sensitivity: {}", config_.mouseSensitivity);
|
E2D_LOG_INFO(" Mouse sensitivity: {}", config_.mouseSensitivity);
|
||||||
E2D_LOG_INFO(" Vibration: {}", config_.enableVibration ? "enabled" : "disabled");
|
E2D_LOG_INFO(" Vibration: {}", config_.enableVibration ? "enabled" : "disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputModule::setWindow(IWindow* window) {
|
||||||
|
if (window_ == window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window_ = window;
|
||||||
|
|
||||||
|
// 如果模块已初始化但还没初始化输入,现在初始化
|
||||||
|
if (isInitialized() && window_ && !input_) {
|
||||||
|
initializeWithWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void InputModule::destroyModule() {
|
void InputModule::destroyModule() {
|
||||||
if (!isInitialized()) {
|
if (!isInitialized()) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -78,3 +100,5 @@ void InputModule::onUpdate(UpdateContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
||||||
|
E2D_MODULE(InputModule, 30, "WindowModule")
|
||||||
|
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
#include <extra2d/modules/logger_module.h>
|
|
||||||
#include <extra2d/utils/logger.h>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
LoggerModule::LoggerModule()
|
|
||||||
: Module()
|
|
||||||
, logLevel_(LogLevel::Info)
|
|
||||||
, consoleOutput_(true)
|
|
||||||
, fileOutput_(false) {
|
|
||||||
}
|
|
||||||
|
|
||||||
LoggerModule::~LoggerModule() {
|
|
||||||
if (isInitialized()) {
|
|
||||||
destroyModule();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoggerModule::setupModule() {
|
|
||||||
if (isInitialized()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::init();
|
|
||||||
|
|
||||||
Logger::setLevel(logLevel_);
|
|
||||||
Logger::setConsoleOutput(consoleOutput_);
|
|
||||||
|
|
||||||
if (fileOutput_ && !logFilePath_.empty()) {
|
|
||||||
Logger::setFileOutput(logFilePath_);
|
|
||||||
}
|
|
||||||
|
|
||||||
setInitialized(true);
|
|
||||||
E2D_LOG_INFO("Logger module initialized");
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoggerModule::destroyModule() {
|
|
||||||
if (!isInitialized()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
E2D_LOG_INFO("Logger module shutting down");
|
|
||||||
Logger::shutdown();
|
|
||||||
setInitialized(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
#include <extra2d/modules/platform_module.h>
|
|
||||||
#include <extra2d/config/config_manager.h>
|
|
||||||
#include <extra2d/utils/logger.h>
|
|
||||||
|
|
||||||
#ifdef __SWITCH__
|
|
||||||
#include <switch.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
PlatformModule::PlatformModule()
|
|
||||||
: Module()
|
|
||||||
, targetPlatform_(PlatformType::Auto)
|
|
||||||
, resolvedPlatform_(PlatformType::Windows) {
|
|
||||||
}
|
|
||||||
|
|
||||||
PlatformModule::~PlatformModule() {
|
|
||||||
if (isInitialized()) {
|
|
||||||
destroyModule();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlatformModule::setupModule() {
|
|
||||||
if (isInitialized()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resolvedPlatform_ == PlatformType::Switch) {
|
|
||||||
if (!initSwitch()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setInitialized(true);
|
|
||||||
E2D_LOG_INFO("Platform module initialized ({})", getPlatformTypeName(resolvedPlatform_));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlatformModule::destroyModule() {
|
|
||||||
if (!isInitialized()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
E2D_LOG_INFO("Platform module shutting down");
|
|
||||||
|
|
||||||
if (resolvedPlatform_ == PlatformType::Switch) {
|
|
||||||
shutdownSwitch();
|
|
||||||
}
|
|
||||||
|
|
||||||
platformConfig_.reset();
|
|
||||||
setInitialized(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PlatformModule::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 PlatformModule::shutdownSwitch() {
|
|
||||||
#ifdef __SWITCH__
|
|
||||||
romfsExit();
|
|
||||||
socketExit();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include <extra2d/modules/render_module.h>
|
#include <extra2d/modules/render_module.h>
|
||||||
|
#include <extra2d/core/module_macros.h>
|
||||||
#include <extra2d/graphics/opengl/gl_shader.h>
|
#include <extra2d/graphics/opengl/gl_shader.h>
|
||||||
#include <extra2d/graphics/shader_manager.h>
|
#include <extra2d/graphics/shader_manager.h>
|
||||||
#include <extra2d/platform/iwindow.h>
|
#include <extra2d/platform/iwindow.h>
|
||||||
|
|
@ -29,8 +30,18 @@ void RenderModule::setupModule() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果 window 还没设置,延迟初始化
|
||||||
|
if (!window_) {
|
||||||
|
E2D_LOG_INFO("Render module waiting for window");
|
||||||
|
setInitialized(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeWithWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderModule::initializeWithWindow() {
|
||||||
if (!window_) {
|
if (!window_) {
|
||||||
E2D_LOG_ERROR("Render module requires window to be set");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,10 +66,21 @@ void RenderModule::setupModule() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setInitialized(true);
|
|
||||||
E2D_LOG_INFO("Render module initialized");
|
E2D_LOG_INFO("Render module initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderModule::setWindow(IWindow* window) {
|
||||||
|
if (window_ == window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window_ = window;
|
||||||
|
|
||||||
|
// 如果模块已初始化但还没初始化渲染器,现在初始化
|
||||||
|
if (isInitialized() && window_ && !renderer_) {
|
||||||
|
initializeWithWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RenderModule::destroyModule() {
|
void RenderModule::destroyModule() {
|
||||||
if (!isInitialized()) {
|
if (!isInitialized()) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -96,3 +118,5 @@ void RenderModule::afterRender(RenderContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
||||||
|
E2D_MODULE(RenderModule, 40, "WindowModule")
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include <extra2d/modules/window_module.h>
|
#include <extra2d/modules/window_module.h>
|
||||||
#include <extra2d/platform/platform_module.h>
|
#include <extra2d/core/module_macros.h>
|
||||||
|
#include <extra2d/platform/backend_factory.h>
|
||||||
#include <extra2d/utils/logger.h>
|
#include <extra2d/utils/logger.h>
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
|
|
@ -119,3 +120,5 @@ bool WindowModule::createWindow(const WindowConfigData& config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
||||||
|
E2D_MODULE(WindowModule, 20, "ConfigModule")
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#include <extra2d/platform/platform_module.h>
|
#include <extra2d/platform/backend_factory.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -12,18 +12,16 @@ void BackendFactory::reg(const std::string& name, WindowFn win, InputFn in) {
|
||||||
}
|
}
|
||||||
|
|
||||||
UniquePtr<IWindow> BackendFactory::createWindow(const std::string& name) {
|
UniquePtr<IWindow> BackendFactory::createWindow(const std::string& name) {
|
||||||
auto& reg = registry();
|
auto it = registry().find(name);
|
||||||
auto it = reg.find(name);
|
if (it != registry().end() && it->second.windowFn) {
|
||||||
if (it != reg.end() && it->second.windowFn) {
|
|
||||||
return it->second.windowFn();
|
return it->second.windowFn();
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
UniquePtr<IInput> BackendFactory::createInput(const std::string& name) {
|
UniquePtr<IInput> BackendFactory::createInput(const std::string& name) {
|
||||||
auto& reg = registry();
|
auto it = registry().find(name);
|
||||||
auto it = reg.find(name);
|
if (it != registry().end() && it->second.inputFn) {
|
||||||
if (it != reg.end() && it->second.inputFn) {
|
|
||||||
return it->second.inputFn();
|
return it->second.inputFn();
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -41,4 +39,4 @@ bool BackendFactory::has(const std::string& name) {
|
||||||
return registry().find(name) != registry().end();
|
return registry().find(name) != registry().end();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include "sdl2_window.h"
|
#include "sdl2_window.h"
|
||||||
#include "sdl2_input.h"
|
#include "sdl2_input.h"
|
||||||
#include <extra2d/platform/platform_module.h>
|
#include <extra2d/platform/backend_factory.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
SDL2Input::SDL2Input() {
|
SDL2Input::SDL2Input()
|
||||||
|
: initialized_(false) {
|
||||||
keyCurrent_.fill(false);
|
keyCurrent_.fill(false);
|
||||||
keyPrevious_.fill(false);
|
keyPrevious_.fill(false);
|
||||||
mouseCurrent_.fill(false);
|
mouseCurrent_.fill(false);
|
||||||
|
|
@ -18,6 +19,8 @@ SDL2Input::~SDL2Input() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDL2Input::init() {
|
void SDL2Input::init() {
|
||||||
|
if (initialized_) return;
|
||||||
|
|
||||||
E2D_LOG_INFO("SDL2Input initialized");
|
E2D_LOG_INFO("SDL2Input initialized");
|
||||||
|
|
||||||
if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0) {
|
if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0) {
|
||||||
|
|
@ -25,10 +28,14 @@ void SDL2Input::init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
openGamepad();
|
openGamepad();
|
||||||
|
initialized_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDL2Input::shutdown() {
|
void SDL2Input::shutdown() {
|
||||||
|
if (!initialized_) return;
|
||||||
|
|
||||||
closeGamepad();
|
closeGamepad();
|
||||||
|
initialized_ = false;
|
||||||
E2D_LOG_INFO("SDL2Input shutdown");
|
E2D_LOG_INFO("SDL2Input shutdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ private:
|
||||||
float rightTrigger_ = 0.0f;
|
float rightTrigger_ = 0.0f;
|
||||||
float deadzone_ = 0.15f;
|
float deadzone_ = 0.15f;
|
||||||
|
|
||||||
|
bool initialized_ = false;
|
||||||
EventCallback eventCallback_;
|
EventCallback eventCallback_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
24
README.md
24
README.md
|
|
@ -134,6 +134,30 @@ flowchart TB
|
||||||
| Input | 输入处理 | 30 |
|
| Input | 输入处理 | 30 |
|
||||||
| Render | 渲染系统 | 40 |
|
| Render | 渲染系统 | 40 |
|
||||||
|
|
||||||
|
### 模块自发现原理
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant C as 编译时
|
||||||
|
participant R as 运行时
|
||||||
|
participant MR as ModuleRegistry
|
||||||
|
participant App as Application
|
||||||
|
|
||||||
|
Note over C: E2D_MODULE 宏展开
|
||||||
|
C->>C: 创建静态变量 ModuleAutoRegister<T>
|
||||||
|
C->>C: 添加 __attribute__((used)) 防止优化
|
||||||
|
|
||||||
|
Note over R: 程序启动
|
||||||
|
R->>MR: 静态初始化阶段
|
||||||
|
MR->>MR: 构造函数自动调用 registerModule()
|
||||||
|
MR->>MR: 模块信息存入注册表
|
||||||
|
|
||||||
|
Note over App: Application::init()
|
||||||
|
App->>MR: getModules()
|
||||||
|
MR->>App: 返回按优先级排序的模块列表
|
||||||
|
App->>App: 按顺序调用 setupModule()
|
||||||
|
```
|
||||||
|
|
||||||
### 服务系统
|
### 服务系统
|
||||||
|
|
||||||
| 服务 | 职责 | 优先级 |
|
| 服务 | 职责 | 优先级 |
|
||||||
|
|
|
||||||
|
|
@ -4,33 +4,267 @@
|
||||||
|
|
||||||
Extra2D 采用模块化架构设计(参考 Kiwano),所有核心功能通过模块系统和服务系统管理。系统提供:
|
Extra2D 采用模块化架构设计(参考 Kiwano),所有核心功能通过模块系统和服务系统管理。系统提供:
|
||||||
|
|
||||||
|
- **自动发现注册**:模块定义即注册,无需手动调用
|
||||||
- **统一的生命周期管理**:初始化、更新、渲染、关闭
|
- **统一的生命周期管理**:初始化、更新、渲染、关闭
|
||||||
- **优先级排序**:确保模块按正确顺序初始化
|
- **优先级排序**:确保模块按正确顺序初始化
|
||||||
- **显式注册**:通过 `Application::use()` 注册模块
|
|
||||||
- **Context 模式**:使用上下文对象遍历模块链
|
- **Context 模式**:使用上下文对象遍历模块链
|
||||||
- **依赖注入**:通过服务定位器解耦模块间依赖
|
- **依赖注入**:通过服务定位器解耦模块间依赖
|
||||||
|
|
||||||
## 架构图
|
## 架构图
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
Application["Application<br/>(协调模块和服务)"]
|
||||||
|
|
||||||
|
Application --> ModuleRegistry
|
||||||
|
Application --> ServiceLocator
|
||||||
|
|
||||||
|
ModuleRegistry["ModuleRegistry<br/>(模块注册器)"]
|
||||||
|
ServiceLocator["ServiceLocator<br/>(服务定位器)"]
|
||||||
|
|
||||||
|
ModuleRegistry --> ConfigModule["ConfigModule"]
|
||||||
|
ModuleRegistry --> WindowModule["WindowModule"]
|
||||||
|
|
||||||
|
ServiceLocator --> SceneService["SceneService"]
|
||||||
|
ServiceLocator --> TimerService["TimerService"]
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ Application │
|
---
|
||||||
│ (协调模块和服务,通过服务定位器获取依赖) │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
## 模块自动注册
|
||||||
│
|
|
||||||
┌───────────────┴───────────────┐
|
### E2D_MODULE 宏
|
||||||
▼ ▼
|
|
||||||
┌─────────────────────────────┐ ┌─────────────────────────────┐
|
模块通过 `E2D_MODULE` 宏自动注册,无需手动调用任何注册函数:
|
||||||
│ Modules │ │ ServiceLocator │
|
|
||||||
│ (模块列表,按优先级排序) │ │ (服务定位器,管理运行时服务) │
|
```cpp
|
||||||
└─────────────────────────────┘ └─────────────────────────────┘
|
// config_module.cpp
|
||||||
│ │
|
#include "config_module.h"
|
||||||
┌─────┴─────┐ ┌───────┴───────┐
|
|
||||||
▼ ▼ ▼ ▼
|
namespace extra2d {
|
||||||
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
|
// 模块实现...
|
||||||
│ Config │ │ Window │ │ Scene │ │ Timer │
|
}
|
||||||
│ Module │ │ Module │ │ Service │ │ Service │
|
|
||||||
└───────────┘ └───────────┘ └───────────┘ └───────────┘
|
// 在文件末尾,namespace 外部
|
||||||
|
E2D_MODULE(ConfigModule, 0) // 名称, 优先级
|
||||||
|
```
|
||||||
|
|
||||||
|
### 带依赖的模块
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// window_module.cpp
|
||||||
|
E2D_MODULE(WindowModule, 20, "ConfigModule") // 依赖 ConfigModule
|
||||||
|
|
||||||
|
// render_module.cpp
|
||||||
|
E2D_MODULE(RenderModule, 40, "WindowModule", "ConfigModule") // 多个依赖
|
||||||
|
```
|
||||||
|
|
||||||
|
### 带属性的模块
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// render_module.cpp
|
||||||
|
E2D_MODULE_BEGIN(RenderModule)
|
||||||
|
E2D_PRIORITY(40)
|
||||||
|
E2D_DEPENDENCIES("WindowModule")
|
||||||
|
E2D_PROPERTY(vsync, bool)
|
||||||
|
E2D_PROPERTY(targetFPS, int)
|
||||||
|
E2D_MODULE_END()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 链接方式详解
|
||||||
|
|
||||||
|
### 静态链接 vs 动态链接
|
||||||
|
|
||||||
|
| 特性 | 静态链接 | 动态链接 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| **引擎库** | `.a` / `.lib` | `.dll` / `.so` |
|
||||||
|
| **模块注册** | 需要 `--whole-archive` 或直接编译 | 自动注册 |
|
||||||
|
| **自定义模块** | 直接编译到可执行文件 | 编译为独立 DLL |
|
||||||
|
| **额外代码** | 无 | 需要 `E2D_FORCE_LINK` |
|
||||||
|
| **分发** | 单一可执行文件 | 需要 DLL 文件 |
|
||||||
|
| **热更新** | 不支持 | 支持(重新加载 DLL) |
|
||||||
|
|
||||||
|
### 静态链接方案(推荐)
|
||||||
|
|
||||||
|
#### 原理
|
||||||
|
|
||||||
|
静态链接时,链接器会优化掉未引用的代码。解决方案:
|
||||||
|
1. **引擎库**:使用 `--whole-archive` 强制链接所有符号
|
||||||
|
2. **自定义模块**:直接编译到可执行文件
|
||||||
|
|
||||||
|
#### xmake 配置
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- 引擎库(静态库)
|
||||||
|
target("extra2d")
|
||||||
|
set_kind("static")
|
||||||
|
-- ... 其他配置
|
||||||
|
|
||||||
|
-- 可执行文件
|
||||||
|
target("demo_basic")
|
||||||
|
set_kind("binary")
|
||||||
|
|
||||||
|
-- 强制链接引擎静态库
|
||||||
|
add_ldflags("-Wl,--whole-archive", {force = true})
|
||||||
|
add_deps("extra2d")
|
||||||
|
add_ldflags("-Wl,--no-whole-archive", {force = true})
|
||||||
|
|
||||||
|
add_files("main.cpp")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 自定义模块配置
|
||||||
|
|
||||||
|
```lua
|
||||||
|
target("demo_hello_module")
|
||||||
|
set_kind("binary")
|
||||||
|
|
||||||
|
-- 强制链接引擎静态库
|
||||||
|
add_ldflags("-Wl,--whole-archive", {force = true})
|
||||||
|
add_deps("extra2d")
|
||||||
|
add_ldflags("-Wl,--no-whole-archive", {force = true})
|
||||||
|
|
||||||
|
-- 直接编译模块源文件到可执行文件
|
||||||
|
add_files("main.cpp", "hello_module.cpp")
|
||||||
|
add_includedirs("hello_module")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 模块定义
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// hello_module.cpp
|
||||||
|
#include "hello_module.h"
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
// 模块实现...
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在文件末尾,namespace 外部
|
||||||
|
E2D_MODULE(HelloModule, 1000)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### main.cpp
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include "hello_module.h"
|
||||||
|
#include <extra2d/extra2d.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
extra2d::Application& app = extra2d::Application::get();
|
||||||
|
|
||||||
|
// 无需任何注册代码!
|
||||||
|
// 模块在静态初始化时自动注册
|
||||||
|
|
||||||
|
extra2d::AppConfig config;
|
||||||
|
config.appName = "My App";
|
||||||
|
|
||||||
|
if (!app.init(config)) return 1;
|
||||||
|
app.run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 动态链接方案
|
||||||
|
|
||||||
|
#### xmake 配置
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- 引擎库(动态库)
|
||||||
|
target("extra2d")
|
||||||
|
set_kind("shared")
|
||||||
|
add_defines("E2D_BUILDING_DLL", {public = false})
|
||||||
|
-- ... 其他配置
|
||||||
|
|
||||||
|
-- 自定义模块 DLL
|
||||||
|
target("hello_module_lib")
|
||||||
|
set_kind("shared")
|
||||||
|
add_defines("E2D_BUILDING_DLL", {public = false})
|
||||||
|
add_deps("extra2d")
|
||||||
|
add_files("hello_module.cpp")
|
||||||
|
add_includedirs("hello_module", {public = true})
|
||||||
|
|
||||||
|
-- 可执行文件
|
||||||
|
target("demo_hello_module")
|
||||||
|
set_kind("binary")
|
||||||
|
add_deps("hello_module_lib")
|
||||||
|
add_files("main.cpp")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 模块定义
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// hello_module.cpp
|
||||||
|
#include "hello_module.h"
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
// 模块实现...
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 E2D_MODULE_EXPORT(自动生成导出函数)
|
||||||
|
E2D_MODULE_EXPORT(HelloModule, 1000)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### main.cpp
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include "hello_module.h"
|
||||||
|
#include <extra2d/extra2d.h>
|
||||||
|
|
||||||
|
// 声明外部模块的导出函数
|
||||||
|
E2D_DECLARE_FORCE_LINK(HelloModule);
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// 触发 DLL 加载
|
||||||
|
E2D_CALL_FORCE_LINK(HelloModule);
|
||||||
|
|
||||||
|
extra2d::Application& app = extra2d::Application::get();
|
||||||
|
if (!app.init(config)) return 1;
|
||||||
|
app.run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 平台兼容性
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
```lua
|
||||||
|
add_ldflags("-Wl,--whole-archive", {force = true})
|
||||||
|
add_deps("extra2d")
|
||||||
|
add_ldflags("-Wl,--no-whole-archive", {force = true})
|
||||||
|
```
|
||||||
|
|
||||||
|
### macOS
|
||||||
|
|
||||||
|
macOS 使用 `-force_load` 代替 `--whole-archive`:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
if is_plat("macosx") then
|
||||||
|
add_ldflags("-force_load", {force = true})
|
||||||
|
end
|
||||||
|
add_deps("extra2d")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windows (MSVC)
|
||||||
|
|
||||||
|
MSVC 使用 `/WHOLEARCHIVE`:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
if is_plat("windows") and is_toolchain("msvc") then
|
||||||
|
add_ldflags("/WHOLEARCHIVE:extra2d", {force = true})
|
||||||
|
end
|
||||||
|
add_deps("extra2d")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windows (MinGW)
|
||||||
|
|
||||||
|
```lua
|
||||||
|
add_ldflags("-Wl,--whole-archive", {force = true})
|
||||||
|
add_deps("extra2d")
|
||||||
|
add_ldflags("-Wl,--no-whole-archive", {force = true})
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -139,7 +373,7 @@ class EventContext : public ModuleContext {
|
||||||
|-----|--------------|---------------|
|
|-----|--------------|---------------|
|
||||||
| 用途 | 平台级初始化 | 运行时功能 |
|
| 用途 | 平台级初始化 | 运行时功能 |
|
||||||
| 生命周期 | Application 管理 | ServiceLocator 管理 |
|
| 生命周期 | Application 管理 | ServiceLocator 管理 |
|
||||||
| 注册方式 | `app.use(module)` | `locator.registerService()` |
|
| 注册方式 | 自动发现 | `locator.registerService()` |
|
||||||
| 可替换性 | 编译时确定 | 运行时可替换 |
|
| 可替换性 | 编译时确定 | 运行时可替换 |
|
||||||
| 示例 | Window, Render, Input | Scene, Timer, Event, Camera |
|
| 示例 | Window, Render, Input | Scene, Timer, Event, Camera |
|
||||||
|
|
||||||
|
|
@ -164,116 +398,78 @@ class EventContext : public ModuleContext {
|
||||||
|
|
||||||
## 创建新模块
|
## 创建新模块
|
||||||
|
|
||||||
### 简单示例
|
### 完整示例(静态链接)
|
||||||
|
|
||||||
|
**hello_module.h:**
|
||||||
```cpp
|
```cpp
|
||||||
#include <extra2d/core/module.h>
|
|
||||||
|
|
||||||
class MyModule : public extra2d::Module {
|
|
||||||
public:
|
|
||||||
const char* getName() const override { return "MyModule"; }
|
|
||||||
|
|
||||||
int getPriority() const override { return 1000; }
|
|
||||||
|
|
||||||
void setupModule() override {
|
|
||||||
// 初始化资源
|
|
||||||
extra2d::E2D_LOG_INFO("MyModule initialized");
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroyModule() override {
|
|
||||||
// 清理资源
|
|
||||||
extra2d::E2D_LOG_INFO("MyModule destroyed");
|
|
||||||
}
|
|
||||||
|
|
||||||
void onUpdate(extra2d::UpdateContext& ctx) override {
|
|
||||||
// 更新逻辑
|
|
||||||
ctx.next(); // 继续下一个模块
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### 注册模块
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
int main() {
|
|
||||||
auto& app = extra2d::Application::get();
|
|
||||||
|
|
||||||
MyModule myModule;
|
|
||||||
app.use(myModule); // 显式注册
|
|
||||||
|
|
||||||
app.init();
|
|
||||||
app.run();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 带配置的模块
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// my_module.h
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <extra2d/core/module.h>
|
#include <extra2d/core/module.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
struct MyModuleConfig {
|
namespace extra2d {
|
||||||
|
|
||||||
|
struct HelloModuleConfig {
|
||||||
std::string greeting = "Hello!";
|
std::string greeting = "Hello!";
|
||||||
int repeatCount = 1;
|
int repeatCount = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MyModule : public extra2d::Module {
|
class HelloModule : public Module {
|
||||||
public:
|
public:
|
||||||
MyModule();
|
HelloModule();
|
||||||
~MyModule() override;
|
~HelloModule() override;
|
||||||
|
|
||||||
const char* getName() const override { return "MyModule"; }
|
const char* getName() const override { return "HelloModule"; }
|
||||||
int getPriority() const override { return 1000; }
|
int getPriority() const override { return 1000; }
|
||||||
|
|
||||||
void setupModule() override;
|
void setupModule() override;
|
||||||
void destroyModule() override;
|
void destroyModule() override;
|
||||||
void onUpdate(extra2d::UpdateContext& ctx) override;
|
void onUpdate(UpdateContext& ctx) override;
|
||||||
|
|
||||||
void setConfig(const MyModuleConfig& config) { config_ = config; }
|
void setConfig(const HelloModuleConfig& config) { config_ = config; }
|
||||||
void sayHello() const;
|
void sayHello() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MyModuleConfig config_;
|
HelloModuleConfig config_;
|
||||||
float time_ = 0.0f;
|
float time_ = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**hello_module.cpp:**
|
||||||
```cpp
|
```cpp
|
||||||
// my_module.cpp
|
#include "hello_module.h"
|
||||||
#include "my_module.h"
|
|
||||||
#include <extra2d/utils/logger.h>
|
#include <extra2d/utils/logger.h>
|
||||||
|
|
||||||
MyModule::MyModule() : Module() {}
|
namespace extra2d {
|
||||||
|
|
||||||
MyModule::~MyModule() {
|
HelloModule::HelloModule() = default;
|
||||||
|
|
||||||
|
HelloModule::~HelloModule() {
|
||||||
if (isInitialized()) {
|
if (isInitialized()) {
|
||||||
destroyModule();
|
destroyModule();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyModule::setupModule() {
|
void HelloModule::setupModule() {
|
||||||
if (isInitialized()) return;
|
if (isInitialized()) return;
|
||||||
|
|
||||||
setInitialized(true);
|
setInitialized(true);
|
||||||
E2D_LOG_INFO("MyModule initialized");
|
E2D_LOG_INFO("HelloModule initialized");
|
||||||
E2D_LOG_INFO(" Greeting: {}", config_.greeting);
|
E2D_LOG_INFO(" Greeting: {}", config_.greeting);
|
||||||
E2D_LOG_INFO(" Repeat Count: {}", config_.repeatCount);
|
E2D_LOG_INFO(" Repeat Count: {}", config_.repeatCount);
|
||||||
|
|
||||||
sayHello();
|
sayHello();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyModule::destroyModule() {
|
void HelloModule::destroyModule() {
|
||||||
if (!isInitialized()) return;
|
if (!isInitialized()) return;
|
||||||
|
|
||||||
E2D_LOG_INFO("MyModule shutdown");
|
E2D_LOG_INFO("HelloModule shutdown");
|
||||||
setInitialized(false);
|
setInitialized(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyModule::onUpdate(extra2d::UpdateContext& ctx) {
|
void HelloModule::onUpdate(UpdateContext& ctx) {
|
||||||
if (!isInitialized()) {
|
if (!isInitialized()) {
|
||||||
ctx.next();
|
ctx.next();
|
||||||
return;
|
return;
|
||||||
|
|
@ -289,11 +485,49 @@ void MyModule::onUpdate(extra2d::UpdateContext& ctx) {
|
||||||
ctx.next();
|
ctx.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyModule::sayHello() const {
|
void HelloModule::sayHello() const {
|
||||||
for (int i = 0; i < config_.repeatCount; ++i) {
|
for (int i = 0; i < config_.repeatCount; ++i) {
|
||||||
E2D_LOG_INFO("[MyModule] {}", config_.greeting);
|
E2D_LOG_INFO("[HelloModule] {}", config_.greeting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
||||||
|
// 自动注册(在 namespace 外部)
|
||||||
|
E2D_MODULE(HelloModule, 1000)
|
||||||
|
```
|
||||||
|
|
||||||
|
**main.cpp:**
|
||||||
|
```cpp
|
||||||
|
#include "hello_module.h"
|
||||||
|
#include <extra2d/extra2d.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
extra2d::Application& app = extra2d::Application::get();
|
||||||
|
|
||||||
|
extra2d::AppConfig config;
|
||||||
|
config.appName = "Hello Module Demo";
|
||||||
|
|
||||||
|
// 无需手动注册!模块已自动注册
|
||||||
|
|
||||||
|
if (!app.init(config)) return 1;
|
||||||
|
app.run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**xmake.lua:**
|
||||||
|
```lua
|
||||||
|
target("demo_hello_module")
|
||||||
|
set_kind("binary")
|
||||||
|
|
||||||
|
-- 强制链接引擎静态库
|
||||||
|
add_ldflags("-Wl,--whole-archive", {force = true})
|
||||||
|
add_deps("extra2d")
|
||||||
|
add_ldflags("-Wl,--no-whole-archive", {force = true})
|
||||||
|
|
||||||
|
-- 直接编译模块源文件
|
||||||
|
add_files("main.cpp", "hello_module.cpp")
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -314,35 +548,6 @@ config.appVersion = "1.0.0";
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Logger 模块
|
|
||||||
|
|
||||||
**职责**:日志系统初始化
|
|
||||||
|
|
||||||
**优先级**:-1(最先初始化)
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
extra2d::LoggerModule loggerModule;
|
|
||||||
loggerModule.setLogLevel(extra2d::LogLevel::Debug);
|
|
||||||
loggerModule.setFileOutput("app.log");
|
|
||||||
app.use(loggerModule);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Platform 模块
|
|
||||||
|
|
||||||
**职责**:平台检测和平台特定初始化
|
|
||||||
|
|
||||||
**优先级**:10
|
|
||||||
|
|
||||||
**支持平台**:
|
|
||||||
- Windows
|
|
||||||
- Linux
|
|
||||||
- macOS
|
|
||||||
- Nintendo Switch
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Window 模块
|
### Window 模块
|
||||||
|
|
||||||
**职责**:窗口创建和管理
|
**职责**:窗口创建和管理
|
||||||
|
|
@ -351,17 +556,6 @@ app.use(loggerModule);
|
||||||
|
|
||||||
**后端**:统一使用 SDL2
|
**后端**:统一使用 SDL2
|
||||||
|
|
||||||
```cpp
|
|
||||||
extra2d::WindowModule windowModule;
|
|
||||||
extra2d::WindowConfigData windowConfig;
|
|
||||||
windowConfig.title = "My App";
|
|
||||||
windowConfig.width = 1280;
|
|
||||||
windowConfig.height = 720;
|
|
||||||
windowConfig.vsync = true;
|
|
||||||
windowModule.setWindowConfig(windowConfig);
|
|
||||||
app.use(windowModule);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Input 模块
|
### Input 模块
|
||||||
|
|
@ -370,15 +564,6 @@ app.use(windowModule);
|
||||||
|
|
||||||
**优先级**:30
|
**优先级**:30
|
||||||
|
|
||||||
```cpp
|
|
||||||
extra2d::InputModule inputModule;
|
|
||||||
extra2d::InputConfigData inputConfig;
|
|
||||||
inputConfig.deadzone = 0.15f;
|
|
||||||
inputConfig.enableVibration = true;
|
|
||||||
inputModule.setInputConfig(inputConfig);
|
|
||||||
app.use(inputModule);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Render 模块
|
### Render 模块
|
||||||
|
|
@ -387,15 +572,6 @@ app.use(inputModule);
|
||||||
|
|
||||||
**优先级**:40
|
**优先级**:40
|
||||||
|
|
||||||
```cpp
|
|
||||||
extra2d::RenderModule renderModule;
|
|
||||||
extra2d::RenderModuleConfig renderConfig;
|
|
||||||
renderConfig.vsync = true;
|
|
||||||
renderConfig.multisamples = 4;
|
|
||||||
renderModule.setRenderConfig(renderConfig);
|
|
||||||
app.use(renderModule);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 服务系统
|
## 服务系统
|
||||||
|
|
@ -558,48 +734,37 @@ auto triangle = ShapeNode::createFilledTriangle(
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 变换继承
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
auto parent = makeShared<Node>();
|
|
||||||
parent->setPos(100, 100);
|
|
||||||
parent->setRotation(45);
|
|
||||||
|
|
||||||
auto child = makeShared<Node>();
|
|
||||||
child->setPos(50, 0); // 相对于父节点
|
|
||||||
parent->addChild(child);
|
|
||||||
|
|
||||||
// child 会随 parent 一起旋转
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 视口适配系统
|
## 常见问题
|
||||||
|
|
||||||
### ViewportAdapter
|
### Q: 模块没有被注册?
|
||||||
|
|
||||||
```cpp
|
**静态链接:**
|
||||||
enum class ViewportMode {
|
- 确保使用了 `--whole-archive`
|
||||||
AspectRatio, // 保持宽高比,可能有黑边
|
- 自定义模块要直接编译到可执行文件
|
||||||
Stretch, // 拉伸填满整个窗口
|
|
||||||
Center, // 居中显示,不缩放
|
**动态链接:**
|
||||||
Custom // 自定义缩放和偏移
|
- 确保使用了 `E2D_MODULE_EXPORT`
|
||||||
};
|
- 确保调用了 `E2D_CALL_FORCE_LINK`
|
||||||
|
|
||||||
|
### Q: 链接错误 "undefined reference"?
|
||||||
|
|
||||||
|
检查链接顺序,`--whole-archive` 要在 `add_deps` 之前。
|
||||||
|
|
||||||
|
### Q: 模块初始化顺序错误?
|
||||||
|
|
||||||
|
使用 `getPriority()` 控制顺序,数字小的先初始化。
|
||||||
|
|
||||||
|
### Q: 如何调试模块注册?
|
||||||
|
|
||||||
|
查看日志输出:
|
||||||
```
|
```
|
||||||
|
[INFO ] ModuleRegistry: 4 modules registered
|
||||||
### 使用 CameraService 配置视口
|
[INFO ] - ConfigModule (priority: 0)
|
||||||
|
[INFO ] - WindowModule (priority: 20)
|
||||||
```cpp
|
[INFO ] - InputModule (priority: 30)
|
||||||
auto cameraService = app.camera();
|
[INFO ] - RenderModule (priority: 40)
|
||||||
if (cameraService) {
|
|
||||||
extra2d::ViewportConfig vpConfig;
|
|
||||||
vpConfig.logicWidth = 1280.0f;
|
|
||||||
vpConfig.logicHeight = 720.0f;
|
|
||||||
vpConfig.mode = extra2d::ViewportMode::AspectRatio;
|
|
||||||
|
|
||||||
cameraService->setViewportConfig(vpConfig);
|
|
||||||
cameraService->updateViewport(windowWidth, windowHeight);
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -653,3 +818,7 @@ void onUpdate(UpdateContext& ctx) override {
|
||||||
ctx.next();
|
ctx.next();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 4. 静态链接优先
|
||||||
|
|
||||||
|
静态链接更简单,无需额外配置,推荐用于大多数场景。
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
* - 变换(位置、旋转、缩放)
|
* - 变换(位置、旋转、缩放)
|
||||||
* - 形状节点渲染
|
* - 形状节点渲染
|
||||||
* - 输入事件处理
|
* - 输入事件处理
|
||||||
|
*
|
||||||
|
* 模块现在通过 E2D_MODULE 宏自动注册,无需手动调用 app.use()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <extra2d/extra2d.h>
|
#include <extra2d/extra2d.h>
|
||||||
|
|
@ -111,6 +113,8 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
Application &app = Application::get();
|
Application &app = Application::get();
|
||||||
|
|
||||||
|
// 模块已通过 E2D_MODULE 宏自动注册,无需调用 app.use()
|
||||||
|
|
||||||
if (!app.init(config)) {
|
if (!app.init(config)) {
|
||||||
std::cerr << "Failed to initialize application!" << std::endl;
|
std::cerr << "Failed to initialize application!" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -166,9 +170,6 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
app.run();
|
app.run();
|
||||||
|
|
||||||
std::cout << "Shutting down..." << std::endl;
|
|
||||||
app.shutdown();
|
|
||||||
|
|
||||||
std::cout << "Goodbye!" << std::endl;
|
std::cout << "Goodbye!" << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "hello_module.h"
|
#include "hello_module.h"
|
||||||
|
#include <extra2d/core/module_macros.h>
|
||||||
#include <extra2d/utils/logger.h>
|
#include <extra2d/utils/logger.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -77,3 +78,5 @@ void HelloModule::sayHello() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
||||||
|
E2D_MODULE(HelloModule, 1000)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/core/export.h>
|
||||||
#include <extra2d/core/module.h>
|
#include <extra2d/core/module.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
|
@ -20,7 +21,7 @@ struct HelloModuleConfigData {
|
||||||
* 这是一个简单的自定义模块示例,展示如何:
|
* 这是一个简单的自定义模块示例,展示如何:
|
||||||
* 1. 继承 Module 基类
|
* 1. 继承 Module 基类
|
||||||
* 2. 实现生命周期方法
|
* 2. 实现生命周期方法
|
||||||
* 3. 使用 Application::use() 注册模块
|
* 3. 使用 E2D_MODULE 宏自动注册模块
|
||||||
*/
|
*/
|
||||||
class HelloModule : public Module {
|
class HelloModule : public Module {
|
||||||
public:
|
public:
|
||||||
|
|
@ -37,7 +38,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief 获取模块名称
|
* @brief 获取模块名称
|
||||||
*/
|
*/
|
||||||
const char* getName() const override { return "HelloModule"; }
|
const char *getName() const override { return "HelloModule"; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取模块优先级
|
* @brief 获取模块优先级
|
||||||
|
|
@ -57,12 +58,12 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief 更新时
|
* @brief 更新时
|
||||||
*/
|
*/
|
||||||
void onUpdate(UpdateContext& ctx) override;
|
void onUpdate(UpdateContext &ctx) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置配置
|
* @brief 设置配置
|
||||||
*/
|
*/
|
||||||
void setConfig(const HelloModuleConfigData& config) { config_ = config; }
|
void setConfig(const HelloModuleConfigData &config) { config_ = config; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 执行问候操作
|
* @brief 执行问候操作
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,11 @@ public:
|
||||||
Scene::onEnter();
|
Scene::onEnter();
|
||||||
addListener(EventType::KeyPress, [](Event &e) {
|
addListener(EventType::KeyPress, [](Event &e) {
|
||||||
auto &keyEvent = std::get<KeyEvent>(e.data);
|
auto &keyEvent = std::get<KeyEvent>(e.data);
|
||||||
|
auto app = Application::get().getModule<HelloModule>();
|
||||||
|
E2D_LOG_INFO("Module {}", app->getName());
|
||||||
if (keyEvent.scancode == static_cast<int>(Key::Escape)) {
|
if (keyEvent.scancode == static_cast<int>(Key::Escape)) {
|
||||||
e.handled = true;
|
e.handled = true;
|
||||||
E2D_LOG_INFO("ESC !!!exit");
|
E2D_LOG_INFO("ESC pressed, exiting...");
|
||||||
Application::get().quit();
|
Application::get().quit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -33,14 +34,15 @@ private:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 应用程序入口
|
* @brief 应用程序入口
|
||||||
|
*
|
||||||
|
* 静态链接时模块直接编译到可执行文件,自动注册,无需任何额外代码!
|
||||||
*/
|
*/
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
Application &app = Application::get();
|
Application &app = Application::get();
|
||||||
|
|
||||||
HelloModule helloModule;
|
|
||||||
app.use(helloModule);
|
|
||||||
|
|
||||||
AppConfig appConfig;
|
AppConfig appConfig;
|
||||||
appConfig.appName = "HelloModule Example";
|
appConfig.appName = "HelloModule Example";
|
||||||
appConfig.appVersion = "1.0.0";
|
appConfig.appVersion = "1.0.0";
|
||||||
|
|
@ -55,7 +57,5 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
app.run();
|
app.run();
|
||||||
|
|
||||||
app.shutdown();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
35
xmake.lua
35
xmake.lua
|
|
@ -145,7 +145,11 @@ target("demo_basic")
|
||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
set_default(false)
|
set_default(false)
|
||||||
|
|
||||||
|
-- 强制链接整个静态库(保留静态初始化的模块注册变量)
|
||||||
|
add_ldflags("-Wl,--whole-archive", {force = true})
|
||||||
add_deps("extra2d")
|
add_deps("extra2d")
|
||||||
|
add_ldflags("-Wl,--no-whole-archive", {force = true})
|
||||||
|
|
||||||
add_files("examples/basic/main.cpp")
|
add_files("examples/basic/main.cpp")
|
||||||
|
|
||||||
-- 平台配置
|
-- 平台配置
|
||||||
|
|
@ -165,13 +169,42 @@ target("demo_basic")
|
||||||
after_build(install_shaders)
|
after_build(install_shaders)
|
||||||
target_end()
|
target_end()
|
||||||
|
|
||||||
|
-- Hello Module 静态库 - 自定义模块示例
|
||||||
|
target("hello_module_lib")
|
||||||
|
set_kind("static")
|
||||||
|
set_default(false)
|
||||||
|
|
||||||
|
add_deps("extra2d")
|
||||||
|
add_files("examples/hello_module/hello_module.cpp")
|
||||||
|
add_includedirs("examples/hello_module", {public = true})
|
||||||
|
|
||||||
|
-- 平台配置
|
||||||
|
local plat = get_config("plat") or os.host()
|
||||||
|
if plat == "mingw" or plat == "windows" then
|
||||||
|
add_packages("glm", "nlohmann_json", "libsdl2")
|
||||||
|
add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi")
|
||||||
|
elseif plat == "linux" then
|
||||||
|
add_packages("glm", "nlohmann_json", "libsdl2")
|
||||||
|
add_syslinks("GL", "dl", "pthread")
|
||||||
|
elseif plat == "macosx" then
|
||||||
|
add_packages("glm", "nlohmann_json", "libsdl2")
|
||||||
|
add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo")
|
||||||
|
end
|
||||||
|
target_end()
|
||||||
|
|
||||||
-- Hello Module 示例 - 展示如何创建自定义模块
|
-- Hello Module 示例 - 展示如何创建自定义模块
|
||||||
|
-- 注意:静态链接时,自定义模块需要直接编译到可执行文件中
|
||||||
target("demo_hello_module")
|
target("demo_hello_module")
|
||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
set_default(false)
|
set_default(false)
|
||||||
|
|
||||||
|
-- 强制链接引擎静态库
|
||||||
|
add_ldflags("-Wl,--whole-archive", {force = true})
|
||||||
add_deps("extra2d")
|
add_deps("extra2d")
|
||||||
add_files("examples/hello_module/*.cpp")
|
add_ldflags("-Wl,--no-whole-archive", {force = true})
|
||||||
|
|
||||||
|
-- 直接编译模块源文件(静态链接时的推荐方式)
|
||||||
|
add_files("examples/hello_module/main.cpp", "examples/hello_module/hello_module.cpp")
|
||||||
add_includedirs("examples/hello_module")
|
add_includedirs("examples/hello_module")
|
||||||
|
|
||||||
-- 平台配置
|
-- 平台配置
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ end
|
||||||
-- 定义 Extra2D 引擎库目标
|
-- 定义 Extra2D 引擎库目标
|
||||||
function define_extra2d_engine()
|
function define_extra2d_engine()
|
||||||
target("extra2d")
|
target("extra2d")
|
||||||
set_kind("static")
|
set_kind("static") -- 改回静态库
|
||||||
|
|
||||||
-- 引擎核心源文件
|
-- 引擎核心源文件
|
||||||
add_files("Extra2D/src/**.cpp")
|
add_files("Extra2D/src/**.cpp")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue