refactor: 重构 graphics 模块目录结构并简化配置系统

- 重组 graphics 目录,按功能分为 core/camera/shader/texture/memory 子目录
- 移除所有模块级 *_config.h 配置文件,改用模块内部的 Cfg 结构体
- 移除 config_loader 和 debug_config 相关文件
- 简化模块系统,使用 Module 基类替代复杂的 IModuleConfig/IModuleInitializer
- 添加 SDL_GetBasePath() 支持跨平台 shader 路径解析
- 修复日志宏不支持 {} 格式化语法的问题
- 更新文档反映新的目录结构
This commit is contained in:
ChestnutYueyue 2026-02-16 23:14:12 +08:00
parent 6273f3235d
commit 0425425ec7
110 changed files with 1692 additions and 5176 deletions

View File

@ -1,231 +1,143 @@
#pragma once #pragma once
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/core/module.h>
#include <extra2d/core/registry.h>
#include <extra2d/core/service_locator.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/module_config.h>
#include <extra2d/platform/iwindow.h>
#include <string> #include <string>
namespace extra2d { namespace extra2d {
class IWindow;
class IInput; class IInput;
class RenderBackend; class RenderBackend;
class WindowModule;
class RenderModule;
class InputModule;
/** /**
* @brief * @brief
* 使Mock
*/ */
class Application { class Application {
public: public:
/**
* @brief
* @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 使 * @brief
* @tparam T
* @tparam Args
* @return
*/
template<typename T, typename... Args>
T* use(Args&&... args) {
return Registry::instance().use<T>(std::forward<Args>(args)...);
}
/**
* @brief
* @tparam T
* @return
*/
template<typename T>
T* get() const {
return Registry::instance().get<T>();
}
/**
* @brief
* @return true * @return true
*/ */
bool init(); bool init();
/** /**
* @brief 使 * @brief
* @param config * @param config
* @return true * @return true
*/ */
bool init(const AppConfig& config); bool init(const AppConfig& config);
/** /**
* @brief 使 * @brief
* @param configPath
* @return true
*/
bool init(const std::string& configPath);
/**
* @brief
*/ */
void shutdown(); void shutdown();
/** /**
* @brief * @brief
*/ */
void run(); void run();
/** /**
* @brief 退 * @brief 退
*/ */
void quit(); void quit();
/** /**
* @brief * @brief
*/ */
void pause(); void pause();
/** /**
* @brief * @brief
*/ */
void resume(); void resume();
/**
* @brief
* @return true
*/
bool isPaused() const { return paused_; } bool isPaused() const { return paused_; }
/**
* @brief
* @return true
*/
bool isRunning() const { return running_; } bool isRunning() const { return running_; }
/** /**
* @brief * @brief
* @return * @return
*/ */
IWindow& window() { return *window_; } IWindow* window();
/** /**
* @brief * @brief
* @return * @return
*/ */
RenderBackend& renderer(); RenderBackend* renderer();
/** /**
* @brief * @brief
* @return * @return
*/ */
IInput& input(); IInput* input();
/**
* @brief
* @return
*/
SharedPtr<class ISceneService> scenes();
/**
* @brief
* @return
*/
SharedPtr<class ITimerService> timers();
/**
* @brief
* @return
*/
SharedPtr<class IEventService> events();
/**
* @brief
* @return
*/
SharedPtr<class ICameraService> camera();
/** /**
* @brief * @brief
* @param scene * @param scene
*/ */
void enterScene(Ptr<class Scene> scene); void enterScene(Ptr<class Scene> scene);
/**
* @brief
* @return
*/
float deltaTime() const { return deltaTime_; } float deltaTime() const { return deltaTime_; }
/**
* @brief
* @return
*/
float totalTime() const { return totalTime_; } float totalTime() const { return totalTime_; }
/**
* @brief
* @return
*/
int fps() const { return currentFps_; } int fps() const { return currentFps_; }
/**
* @brief
* @return
*/
ConfigManager& config();
/**
* @brief
* @return
*/
const AppConfig& getConfig() const;
/**
* @brief
* @tparam T
* @param service
*/
template<typename T>
void registerService(SharedPtr<T> service) {
ServiceLocator::instance().registerService(service);
}
/**
* @brief
* @tparam T
* @return
*/
template<typename T>
SharedPtr<T> getService() {
return ServiceLocator::instance().getService<T>();
}
private: private:
Application() = default; Application();
~Application(); ~Application();
/**
* @brief
* @return true
*/
bool initModules();
/**
* @brief
*/
void registerCoreServices();
/**
* @brief
*/
void mainLoop(); void mainLoop();
/**
* @brief
*/
void update(); void update();
/**
* @brief
*/
void render(); void render();
void registerCoreServices();
IWindow* window_ = nullptr;
bool initialized_ = false; bool initialized_ = false;
bool running_ = false; bool running_ = false;
bool paused_ = false; bool paused_ = false;
bool shouldQuit_ = false; bool shouldQuit_ = false;
float deltaTime_ = 0.0f; float deltaTime_ = 0.0f;
float totalTime_ = 0.0f; float totalTime_ = 0.0f;
double lastFrameTime_ = 0.0; double lastFrameTime_ = 0.0;
int frameCount_ = 0; int frameCount_ = 0;
float fpsTimer_ = 0.0f; float fpsTimer_ = 0.0f;
int currentFps_ = 0; int currentFps_ = 0;
AppConfig appConfig_;
}; };
} } // namespace extra2d

View File

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

View File

@ -1,200 +0,0 @@
#pragma once
#include <extra2d/config/app_config.h>
#include <extra2d/core/types.h>
#include <string>
namespace extra2d {
/**
* @file config_loader.h
* @brief
*
* AppConfig
* ModuleRegistry IModuleConfig
*/
/**
* @brief
*/
struct ConfigLoadResult {
bool success = false;
std::string errorMessage;
int errorLine = -1;
std::string errorField;
static ConfigLoadResult ok() { return ConfigLoadResult{true, "", -1, ""}; }
static ConfigLoadResult error(const std::string& msg, int line = -1, const std::string& field = "") {
return ConfigLoadResult{false, msg, line, field};
}
bool isOk() const { return success; }
bool hasError() const { return !success; }
};
/**
* @brief
*/
struct ConfigSaveResult {
bool success = false;
std::string errorMessage;
static ConfigSaveResult ok() { return ConfigSaveResult{true, ""}; }
static ConfigSaveResult error(const std::string& msg) {
return ConfigSaveResult{false, msg};
}
bool isOk() const { return success; }
bool hasError() const { return !success; }
};
/**
* @brief
*/
class ConfigLoader {
public:
virtual ~ConfigLoader() = default;
/**
* @brief
* @param filepath
* @param config
* @return
*/
virtual ConfigLoadResult load(const std::string& filepath, AppConfig& config) = 0;
/**
* @brief
* @param filepath
* @param config
* @return
*/
virtual ConfigSaveResult save(const std::string& filepath, const AppConfig& config) = 0;
/**
* @brief
* @param content
* @param config
* @return
*/
virtual ConfigLoadResult loadFromString(const std::string& content, AppConfig& config) = 0;
/**
* @brief
* @param config
* @return
*/
virtual std::string saveToString(const AppConfig& config) = 0;
/**
* @brief
* @param filepath
* @return
*/
virtual ConfigLoadResult loadWithModules(const std::string& filepath) = 0;
/**
* @brief
* @param filepath
* @return
*/
virtual ConfigSaveResult saveWithModules(const std::string& filepath) = 0;
/**
* @brief
* @return "json"
*/
virtual const char* extension() const = 0;
/**
* @brief
* @param filepath
* @return true
*/
virtual bool supportsFile(const std::string& filepath) const = 0;
/**
* @brief
* @return
*/
virtual UniquePtr<ConfigLoader> clone() const = 0;
};
/**
* @brief JSON
*/
class JsonConfigLoader : public ConfigLoader {
public:
JsonConfigLoader() = default;
~JsonConfigLoader() override = default;
ConfigLoadResult load(const std::string& filepath, AppConfig& config) override;
ConfigSaveResult save(const std::string& filepath, const AppConfig& config) override;
ConfigLoadResult loadFromString(const std::string& content, AppConfig& config) override;
std::string saveToString(const AppConfig& config) override;
ConfigLoadResult loadWithModules(const std::string& filepath) override;
ConfigSaveResult saveWithModules(const std::string& filepath) override;
const char* extension() const override { return "json"; }
bool supportsFile(const std::string& filepath) const override;
UniquePtr<ConfigLoader> clone() const override;
};
/**
* @brief INI
*/
class IniConfigLoader : public ConfigLoader {
public:
IniConfigLoader() = default;
~IniConfigLoader() override = default;
ConfigLoadResult load(const std::string& filepath, AppConfig& config) override;
ConfigSaveResult save(const std::string& filepath, const AppConfig& config) override;
ConfigLoadResult loadFromString(const std::string& content, AppConfig& config) override;
std::string saveToString(const AppConfig& config) override;
ConfigLoadResult loadWithModules(const std::string& filepath) override;
ConfigSaveResult saveWithModules(const std::string& filepath) override;
const char* extension() const override { return "ini"; }
bool supportsFile(const std::string& filepath) const override;
UniquePtr<ConfigLoader> clone() const override;
private:
std::string sectionKey(const std::string& section, const std::string& key) const;
ConfigLoadResult parseInt(const std::string& value, int& result, const std::string& fieldName);
ConfigLoadResult parseFloat(const std::string& value, float& result, const std::string& fieldName);
ConfigLoadResult parseBool(const std::string& value, bool& result, const std::string& fieldName);
};
/**
* @brief
*/
class ConfigLoaderFactory {
public:
/**
* @brief
* @param extension
* @return nullptr
*/
static UniquePtr<ConfigLoader> create(const std::string& extension);
/**
* @brief
* @param filepath
* @return nullptr
*/
static UniquePtr<ConfigLoader> createForFile(const std::string& filepath);
/**
* @brief
* @param extension
* @return true
*/
static bool isExtensionSupported(const std::string& extension);
/**
* @brief
* @return
*/
static std::vector<std::string> getSupportedExtensions();
};
}

View File

@ -1,295 +0,0 @@
#pragma once
#include <extra2d/config/app_config.h>
#include <extra2d/config/config_loader.h>
#include <extra2d/config/platform_config.h>
#include <extra2d/core/types.h>
#include <mutex>
#include <string>
#include <unordered_map>
namespace extra2d {
/**
* @file config_manager.h
* @brief
*
* AppConfig
* ModuleRegistry IModuleConfig
*/
/**
* @brief
*/
struct ConfigChangeEvent {
std::string section;
std::string field;
std::string oldValue;
std::string newValue;
};
/**
* @brief
*/
using ConfigChangeCallback = Function<void(const ConfigChangeEvent &)>;
/**
* @brief
*/
class ConfigManager {
public:
/**
* @brief
* @return
*/
static ConfigManager &instance();
/**
* @brief
* @param configPath
* @return true
*/
bool initialize(const std::string &configPath = "config.json");
/**
* @brief
*/
void shutdown();
/**
* @brief
* @return true
*/
bool isInitialized() const;
/**
* @brief
* @param filepath 使
* @return
*/
ConfigLoadResult loadConfig(const std::string &filepath = "");
/**
* @brief
* @param filepath 使
* @return
*/
ConfigSaveResult saveConfig(const std::string &filepath = "");
/**
* @brief
* @param filepath
* @return
*/
ConfigLoadResult loadConfigWithModules(const std::string &filepath = "");
/**
* @brief
* @param filepath
* @return
*/
ConfigSaveResult saveConfigWithModules(const std::string &filepath = "");
/**
* @brief
* @return
*/
ConfigLoadResult reload();
/**
* @brief
* @return
*/
const AppConfig &appConfig() const;
/**
* @brief
* @return
*/
AppConfig &appConfig();
/**
* @brief
* @param config
*/
void setAppConfig(const AppConfig &config);
/**
* @brief
* @return
*/
PlatformConfig *platformConfig();
/**
* @brief
* @return
*/
const PlatformConfig *platformConfig() const;
/**
* @brief
* @param callback
* @return ID
*/
int registerChangeCallback(ConfigChangeCallback callback);
/**
* @brief
* @param callbackId ID
*/
void unregisterChangeCallback(int callbackId);
/**
* @brief
*/
void clearChangeCallbacks();
/**
* @brief
* @param section
* @param key
* @param value
*/
void setValue(const std::string &section, const std::string &key,
const std::string &value);
/**
* @brief
* @param section
* @param key
* @param value
*/
void setValue(const std::string &section, const std::string &key, int value);
/**
* @brief
* @param section
* @param key
* @param value
*/
void setValue(const std::string &section, const std::string &key,
float value);
/**
* @brief
* @param section
* @param key
* @param value
*/
void setValue(const std::string &section, const std::string &key, bool value);
/**
* @brief
* @param section
* @param key
* @param defaultValue
* @return
*/
std::string getValue(const std::string &section, const std::string &key,
const std::string &defaultValue = "") const;
/**
* @brief
* @param section
* @param key
* @param defaultValue
* @return
*/
int getIntValue(const std::string &section, const std::string &key,
int defaultValue = 0) const;
/**
* @brief
* @param section
* @param key
* @param defaultValue
* @return
*/
float getFloatValue(const std::string &section, const std::string &key,
float defaultValue = 0.0f) const;
/**
* @brief
* @param section
* @param key
* @param defaultValue
* @return
*/
bool getBoolValue(const std::string &section, const std::string &key,
bool defaultValue = false) const;
/**
* @brief
*/
void resetToDefaults();
/**
* @brief
* @return true
*/
bool hasUnsavedChanges() const;
/**
* @brief
*/
void markModified();
/**
* @brief
*/
void clearModified();
/**
* @brief
* @return
*/
const std::string &configPath() const { return m_configPath; }
/**
* @brief
* @param enabled
* @param interval
*/
void setAutoSave(bool enabled, float interval = 30.0f);
/**
* @brief
* @return true
*/
bool isAutoSaveEnabled() const { return m_autoSaveEnabled; }
/**
* @brief
* @param deltaTime
*/
void update(float deltaTime);
private:
ConfigManager();
~ConfigManager();
ConfigManager(const ConfigManager &) = delete;
ConfigManager &operator=(const ConfigManager &) = delete;
void notifyChangeCallbacks(const ConfigChangeEvent &event);
AppConfig m_appConfig;
UniquePtr<PlatformConfig> m_platformConfig;
UniquePtr<ConfigLoader> m_loader;
std::string m_configPath;
bool m_initialized = false;
bool m_modified = false;
mutable std::mutex m_mutex;
std::unordered_map<int, ConfigChangeCallback> m_changeCallbacks;
int m_nextCallbackId = 1;
std::unordered_map<std::string, std::string> m_rawValues;
bool m_autoSaveEnabled = false;
float m_autoSaveInterval = 30.0f;
float m_autoSaveTimer = 0.0f;
};
#define CONFIG_MANAGER ConfigManager::instance()
}

View File

@ -1,70 +0,0 @@
#pragma once
#include <extra2d/config/module_config.h>
#include <extra2d/config/module_initializer.h>
#include <extra2d/config/app_config.h>
#include <extra2d/config/config_manager.h>
#include <string>
namespace extra2d {
class ConfigModuleConfig : public IModuleConfig {
public:
std::string configPath;
AppConfig appConfig;
ModuleInfo getModuleInfo() const override {
ModuleInfo info;
info.id = 0;
info.name = "Config";
info.version = "1.0.0";
info.priority = ModulePriority::Core;
info.enabled = true;
return info;
}
std::string getConfigSectionName() const override {
return "config";
}
bool validate() const override {
return true;
}
void resetToDefaults() override {
configPath.clear();
appConfig = AppConfig{};
}
bool loadFromJson(const void* jsonData) override;
bool saveToJson(void* jsonData) const override;
};
class ConfigModuleInitializer : public IModuleInitializer {
public:
ConfigModuleInitializer();
~ConfigModuleInitializer() override;
ModuleId getModuleId() const override { return moduleId_; }
ModulePriority getPriority() const override { return ModulePriority::Core; }
std::vector<ModuleId> getDependencies() const override { return {}; }
bool initialize(const IModuleConfig* config) override;
void shutdown() override;
bool isInitialized() const override { return initialized_; }
void setModuleId(ModuleId id) { moduleId_ = id; }
void setAppConfig(const AppConfig& config) { appConfig_ = config; }
void setConfigPath(const std::string& path) { configPath_ = path; }
private:
ModuleId moduleId_ = INVALID_MODULE_ID;
bool initialized_ = false;
AppConfig appConfig_;
std::string configPath_;
};
ModuleId get_config_module_id();
void register_config_module();
} // namespace extra2d

View File

@ -1,103 +0,0 @@
#pragma once
#include <extra2d/config/platform_config.h>
#include <extra2d/core/types.h>
#include <string>
namespace extra2d {
/**
* @brief
*/
using ModuleId = uint32_t;
/**
* @brief
*/
constexpr ModuleId INVALID_MODULE_ID = 0;
/**
* @brief
*
*/
enum class ModulePriority : int {
Core = 0, ///< 核心模块(最先初始化)
Platform = 100, ///< 平台相关模块
Graphics = 200, ///< 图形渲染模块
Audio = 300, ///< 音频模块
Input = 400, ///< 输入模块
Resource = 500, ///< 资源管理模块
Game = 1000, ///< 游戏逻辑模块
User = 2000 ///< 用户自定义模块
};
/**
* @brief
*
*/
struct ModuleInfo {
ModuleId id = INVALID_MODULE_ID; ///< 模块唯一标识符
std::string name; ///< 模块名称
std::string version; ///< 模块版本号
ModulePriority priority = ModulePriority::User; ///< 模块优先级
bool enabled = true; ///< 是否启用
};
/**
* @brief
*
*/
class IModuleConfig {
public:
/**
* @brief
*/
virtual ~IModuleConfig() = default;
/**
* @brief
* @return
*/
virtual ModuleInfo getModuleInfo() const = 0;
/**
* @brief
*
* @return
*/
virtual std::string getConfigSectionName() const = 0;
/**
* @brief
* @return true
*/
virtual bool validate() const { return true; }
/**
* @brief
*
* @param platform
*/
virtual void applyPlatformConstraints(PlatformType platform) { }
/**
* @brief
*/
virtual void resetToDefaults() = 0;
/**
* @brief JSON
* @param jsonData JSON
* @return true
*/
virtual bool loadFromJson(const void* jsonData) { return true; }
/**
* @brief JSON
* @param jsonData JSON
* @return true
*/
virtual bool saveToJson(void* jsonData) const { return true; }
};
} // namespace extra2d

View File

@ -1,63 +0,0 @@
#pragma once
#include <extra2d/config/module_config.h>
#include <functional>
#include <vector>
namespace extra2d {
/**
* @brief
*
*/
class IModuleInitializer {
public:
/**
* @brief
*/
virtual ~IModuleInitializer() = default;
/**
* @brief
* @return
*/
virtual ModuleId getModuleId() const = 0;
/**
* @brief
* @return
*/
virtual ModulePriority getPriority() const = 0;
/**
* @brief
*
* @return
*/
virtual std::vector<ModuleId> getDependencies() const { return {}; }
/**
* @brief
* @param config
* @return true
*/
virtual bool initialize(const IModuleConfig* config) = 0;
/**
* @brief
*/
virtual void shutdown() = 0;
/**
* @brief
* @return true
*/
virtual bool isInitialized() const = 0;
};
/**
* @brief
*/
using ModuleInitializerFactory = std::function<UniquePtr<IModuleInitializer>()>;
} // namespace extra2d

View File

@ -1,156 +0,0 @@
#pragma once
#include <extra2d/config/module_config.h>
#include <extra2d/config/module_initializer.h>
#include <unordered_map>
#include <vector>
#include <mutex>
namespace extra2d {
/**
* @brief
*
*/
struct ModuleEntry {
ModuleId id; ///< 模块标识符
UniquePtr<IModuleConfig> config; ///< 模块配置
ModuleInitializerFactory initializerFactory;///< 初始化器工厂函数
UniquePtr<IModuleInitializer> initializer; ///< 初始化器实例
bool initialized = false; ///< 是否已初始化
};
/**
* @brief
*
*/
class ModuleRegistry {
public:
/**
* @brief
* @return
*/
static ModuleRegistry& instance();
/**
* @brief
*/
ModuleRegistry(const ModuleRegistry&) = delete;
/**
* @brief
*/
ModuleRegistry& operator=(const ModuleRegistry&) = delete;
/**
* @brief
* @param config
* @param initializerFactory
* @return
*/
ModuleId registerModule(
UniquePtr<IModuleConfig> config,
ModuleInitializerFactory initializerFactory = nullptr
);
/**
* @brief
* @param id
* @return true
*/
bool unregisterModule(ModuleId id);
/**
* @brief
* @param id
* @return nullptr
*/
IModuleConfig* getModuleConfig(ModuleId id) const;
/**
* @brief
* @param name
* @return nullptr
*/
IModuleConfig* getModuleConfigByName(const std::string& name) const;
/**
* @brief
* @param id
* @return nullptr
*/
IModuleInitializer* getInitializer(ModuleId id);
/**
* @brief
* @return
*/
std::vector<ModuleId> getAllModules() const;
/**
* @brief
*
* @return
*/
std::vector<ModuleId> getInitializationOrder() const;
/**
* @brief
* @param id
* @return true
*/
bool hasModule(ModuleId id) const;
/**
* @brief
*/
void clear();
/**
* @brief
* @return
*/
size_t size() const { return modules_.size(); }
private:
/**
* @brief
*/
ModuleRegistry() = default;
/**
* @brief
*/
~ModuleRegistry() = default;
/**
* @brief
* @return
*/
ModuleId generateId();
std::unordered_map<ModuleId, ModuleEntry> modules_; ///< 模块注册表
std::unordered_map<std::string, ModuleId> nameToId_; ///< 名称到标识符映射
mutable std::mutex mutex_; ///< 线程安全互斥锁
ModuleId nextId_ = 1; ///< 下一个可用标识符
};
/**
* @brief
* 使
*
* @example
* E2D_REGISTER_MODULE(MyModuleConfig, MyModuleInitializer)
*/
#define E2D_REGISTER_MODULE(ConfigClass, InitializerClass) \
namespace { \
static const ::extra2d::ModuleId E2D_ANONYMOUS_VAR(module_id_) = \
::extra2d::ModuleRegistry::instance().registerModule( \
::extra2d::makeUnique<ConfigClass>(), \
[]() -> ::extra2d::UniquePtr<::extra2d::IModuleInitializer> { \
return ::extra2d::makeUnique<InitializerClass>(); \
} \
); \
}
} // namespace extra2d

View File

@ -0,0 +1,76 @@
#pragma once
#include <extra2d/core/types.h>
#include <string>
#include <vector>
#include <typeindex>
namespace extra2d {
class Application;
/**
* @brief
*
*/
class Module {
public:
virtual ~Module() = default;
/**
* @brief
* @return true
*/
virtual bool init() = 0;
/**
* @brief
*/
virtual void shutdown() = 0;
/**
* @brief
* @return true
*/
virtual bool ok() const = 0;
/**
* @brief
* @return
*/
virtual const char* name() const = 0;
/**
* @brief
* @return
*/
virtual int priority() const { return 100; }
/**
* @brief
* @return
*/
virtual std::vector<std::type_index> deps() const { return {}; }
/**
* @brief Application
* @param app Application指针
*/
void setApp(class Application* app) { app_ = app; }
/**
* @brief Application
* @return Application指针
*/
class Application* app() const { return app_; }
protected:
class Application* app_ = nullptr;
};
/**
* @brief
*/
using ModuleFactory = std::function<UniquePtr<Module>()>;
} // namespace extra2d

View File

@ -0,0 +1,116 @@
#pragma once
#include <extra2d/core/module.h>
#include <extra2d/core/types.h>
#include <unordered_map>
#include <typeindex>
#include <vector>
#include <memory>
namespace extra2d {
class Application;
/**
* @brief
*
*/
class Registry {
public:
static Registry& instance();
Registry(const Registry&) = delete;
Registry& operator=(const Registry&) = delete;
/**
* @brief
* @tparam T
* @tparam Args
* @param args
* @return
*/
template<typename T, typename... Args>
T* use(Args&&... args) {
static_assert(std::is_base_of_v<Module, T>, "T must derive from Module");
auto typeIdx = std::type_index(typeid(T));
if (modules_.count(typeIdx)) {
return static_cast<T*>(modules_[typeIdx].get());
}
auto module = makeUnique<T>(std::forward<Args>(args)...);
T* ptr = module.get();
module->setApp(app_);
modules_[typeIdx] = std::move(module);
return ptr;
}
/**
* @brief
* @tparam T
* @return nullptr
*/
template<typename T>
T* get() const {
auto typeIdx = std::type_index(typeid(T));
auto it = modules_.find(typeIdx);
if (it != modules_.end()) {
return static_cast<T*>(it->second.get());
}
return nullptr;
}
/**
* @brief
* @param typeIdx
* @return
*/
Module* get(std::type_index typeIdx) const {
auto it = modules_.find(typeIdx);
if (it != modules_.end()) {
return it->second.get();
}
return nullptr;
}
/**
* @brief Application
*/
void setApp(Application* app) { app_ = app; }
/**
* @brief
* @return true
*/
bool init();
/**
* @brief
*/
void shutdown();
/**
* @brief
*/
void clear();
/**
* @brief
*/
size_t size() const { return modules_.size(); }
private:
Registry() = default;
~Registry() = default;
/**
* @brief
* @return
*/
std::vector<Module*> topologicalSort();
std::unordered_map<std::type_index, UniquePtr<Module>> modules_;
Application* app_ = nullptr;
};
} // namespace extra2d

View File

@ -1,53 +0,0 @@
#pragma once
#include <string>
#include <vector>
namespace extra2d {
/**
* @file debug_config.h
* @brief
*
* DebugModule
*/
/**
* @brief
*/
struct DebugConfigData {
bool enabled = false;
bool showFPS = false;
bool showMemoryUsage = false;
bool showRenderStats = false;
bool showColliders = false;
bool showGrid = false;
bool logToFile = false;
bool logToConsole = true;
int logLevel = 2;
bool breakOnAssert = true;
bool enableProfiling = false;
std::string logFilePath;
std::vector<std::string> debugFlags;
/**
* @brief
* @param flag
* @return true
*/
bool hasDebugFlag(const std::string& flag) const;
/**
* @brief
* @param flag
*/
void addDebugFlag(const std::string& flag);
/**
* @brief
* @param flag
*/
void removeDebugFlag(const std::string& flag);
};
}

View File

@ -7,14 +7,11 @@
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/core/module.h>
#include <extra2d/core/registry.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_manager.h>
#include <extra2d/config/module_config.h>
#include <extra2d/config/module_initializer.h>
#include <extra2d/config/module_registry.h>
#include <extra2d/config/platform_config.h> #include <extra2d/config/platform_config.h>
#include <extra2d/config/platform_detector.h> #include <extra2d/config/platform_detector.h>
@ -23,21 +20,20 @@
#include <extra2d/platform/iwindow.h> #include <extra2d/platform/iwindow.h>
#include <extra2d/platform/keys.h> #include <extra2d/platform/keys.h>
#include <extra2d/platform/input_module.h> #include <extra2d/platform/input_module.h>
#include <extra2d/platform/platform_module.h> #include <extra2d/platform/backend_factory.h>
#include <extra2d/platform/window_module.h> #include <extra2d/platform/window_module.h>
// Graphics // Graphics
#include <extra2d/graphics/camera.h> #include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/font.h> #include <extra2d/graphics/texture/font.h>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/shader_manager.h> #include <extra2d/graphics/core/render_module.h>
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/graphics/texture/texture.h>
#include <extra2d/graphics/render_target.h> #include <extra2d/graphics/core/render_target.h>
#include <extra2d/graphics/viewport_adapter.h> #include <extra2d/graphics/camera/viewport_adapter.h>
#include <extra2d/graphics/vram_manager.h> #include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/graphics/texture/texture_pool.h>
#include <extra2d/graphics/texture_pool.h>
// Scene // Scene
#include <extra2d/scene/node.h> #include <extra2d/scene/node.h>
@ -52,7 +48,6 @@
#include <extra2d/event/event_queue.h> #include <extra2d/event/event_queue.h>
// Utils // Utils
#include <extra2d/utils/logger.h>
#include <extra2d/utils/random.h> #include <extra2d/utils/random.h>
#include <extra2d/utils/timer.h> #include <extra2d/utils/timer.h>
@ -61,6 +56,7 @@
#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> #include <extra2d/services/camera_service.h>
#include <extra2d/services/logger_service.h>
// Application // Application
#include <extra2d/app/application.h> #include <extra2d/app/application.h>

View File

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

View File

@ -0,0 +1,72 @@
#pragma once
#include <extra2d/core/module.h>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/platform/window_module.h>
#include <typeindex>
namespace extra2d {
/**
* @brief
*
*/
class RenderModule : public Module {
public:
/**
* @brief
*/
struct Cfg {
BackendType backend;
int targetFPS;
bool vsync;
int multisamples;
int priority;
Cfg()
: backend(BackendType::OpenGL)
, targetFPS(60)
, vsync(true)
, multisamples(0)
, priority(10)
{}
};
/**
* @brief
* @param cfg
*/
explicit RenderModule(const Cfg& cfg = Cfg{});
/**
* @brief
*/
~RenderModule() override;
bool init() override;
void shutdown() override;
bool ok() const override { return initialized_; }
const char* name() const override { return "render"; }
int priority() const override { return cfg_.priority; }
/**
* @brief
* @return
*/
std::vector<std::type_index> deps() const override {
return {std::type_index(typeid(WindowModule))};
}
/**
* @brief
* @return
*/
RenderBackend* renderer() const { return renderer_.get(); }
private:
Cfg cfg_;
UniquePtr<RenderBackend> renderer_;
bool initialized_ = false;
};
} // namespace extra2d

View File

@ -3,7 +3,7 @@
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/graphics/opengl/gl_texture.h> #include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/texture/texture.h>
#include <mutex> #include <mutex>
namespace extra2d { namespace extra2d {

View File

@ -3,9 +3,9 @@
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/graphics/font.h> #include <extra2d/graphics/texture/font.h>
#include <extra2d/graphics/opengl/gl_texture.h> #include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/texture/texture.h>
#include <memory> #include <memory>
#include <stb/stb_rect_pack.h> #include <stb/stb_rect_pack.h>
#include <stb/stb_truetype.h> #include <stb/stb_truetype.h>

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <extra2d/graphics/opengl/gl_sprite_batch.h> #include <extra2d/graphics/opengl/gl_sprite_batch.h>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/shader_interface.h> #include <extra2d/graphics/shader/shader_interface.h>
#include <array> #include <array>
#include <glad/glad.h> #include <glad/glad.h>

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/graphics/shader_interface.h> #include <extra2d/graphics/shader/shader_interface.h>
#include <glad/glad.h> #include <glad/glad.h>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>

View File

@ -5,7 +5,7 @@
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/graphics/opengl/gl_shader.h> #include <extra2d/graphics/opengl/gl_shader.h>
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/texture/texture.h>
#include <glm/mat4x4.hpp> #include <glm/mat4x4.hpp>
#include <vector> #include <vector>

View File

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

View File

@ -1,51 +0,0 @@
#pragma once
#include <extra2d/core/math_types.h>
#include <extra2d/graphics/render_backend.h>
#include <string>
namespace extra2d {
/**
* @file render_config.h
* @brief
*
* RenderModule
*/
/**
* @brief
*/
struct RenderConfigData {
BackendType backend = BackendType::OpenGL;
int targetFPS = 60;
bool vsync = true;
bool tripleBuffering = false;
int multisamples = 0;
bool sRGBFramebuffer = false;
Color clearColor{0.0f, 0.0f, 0.0f, 1.0f};
int maxTextureSize = 0;
int textureAnisotropy = 1;
bool wireframeMode = false;
bool depthTest = false;
bool blending = true;
bool dithering = false;
int spriteBatchSize = 1000;
int maxRenderTargets = 1;
bool allowShaderHotReload = false;
std::string shaderCachePath;
/**
* @brief
* @return 0 true
*/
bool isMultisampleEnabled() const { return multisamples > 0; }
/**
* @brief
* @return true
*/
bool isFPSCapped() const { return targetFPS > 0; }
};
}

View File

@ -1,66 +0,0 @@
#pragma once
#include <extra2d/config/module_config.h>
#include <extra2d/config/module_initializer.h>
#include <extra2d/graphics/render_config.h>
#include <extra2d/graphics/render_backend.h>
#include <extra2d/core/types.h>
namespace extra2d {
class RenderModuleConfig : public IModuleConfig {
public:
BackendType backend = BackendType::OpenGL;
bool vsync = true;
int targetFPS = 60;
int multisamples = 0;
bool sRGBFramebuffer = false;
int spriteBatchSize = 1000;
ModuleInfo getModuleInfo() const override {
ModuleInfo info;
info.name = "Render";
info.version = "1.0.0";
info.priority = ModulePriority::Graphics;
info.enabled = true;
return info;
}
std::string getConfigSectionName() const override { return "render"; }
bool validate() const override;
void applyPlatformConstraints(PlatformType platform) override;
void resetToDefaults() override;
bool loadFromJson(const void* jsonData) override;
bool saveToJson(void* jsonData) const override;
};
class RenderModuleInitializer : public IModuleInitializer {
public:
RenderModuleInitializer();
~RenderModuleInitializer() override;
ModuleId getModuleId() const override { return moduleId_; }
ModulePriority getPriority() const override { return ModulePriority::Graphics; }
std::vector<ModuleId> getDependencies() const override;
bool initialize(const IModuleConfig* config) override;
void shutdown() override;
bool isInitialized() const override { return initialized_; }
void setModuleId(ModuleId id) { moduleId_ = id; }
void setWindow(IWindow* window) { window_ = window; }
RenderBackend* getRenderer() const { return renderer_.get(); }
private:
ModuleId moduleId_ = INVALID_MODULE_ID;
IWindow* window_ = nullptr;
UniquePtr<RenderBackend> renderer_;
bool initialized_ = false;
};
ModuleId get_render_module_id();
void register_render_module();
} // namespace extra2d

View File

@ -1,10 +1,10 @@
#pragma once #pragma once
#include <extra2d/config/platform_detector.h> #include <extra2d/config/platform_detector.h>
#include <extra2d/graphics/shader_cache.h> #include <extra2d/graphics/shader/shader_cache.h>
#include <extra2d/graphics/shader_hot_reloader.h> #include <extra2d/graphics/shader/shader_hot_reloader.h>
#include <extra2d/graphics/shader_interface.h> #include <extra2d/graphics/shader/shader_interface.h>
#include <extra2d/graphics/shader_loader.h> #include <extra2d/graphics/shader/shader_loader.h>
#include <functional> #include <functional>
#include <unordered_map> #include <unordered_map>

View File

@ -2,7 +2,7 @@
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/graphics/shader_interface.h> #include <extra2d/graphics/shader/shader_interface.h>
#include <glm/vec4.hpp> #include <glm/vec4.hpp>
namespace extra2d { namespace extra2d {

View File

@ -3,7 +3,7 @@
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/texture/texture.h>
#include <extra2d/graphics/opengl/gl_texture.h> #include <extra2d/graphics/opengl/gl_texture.h>
#include <string> #include <string>
#include <vector> #include <vector>

View File

@ -2,7 +2,7 @@
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/texture/texture.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
#include <algorithm> #include <algorithm>

View File

@ -1,37 +0,0 @@
#pragma once
#include <string>
namespace extra2d {
/**
* @file input_config.h
* @brief
*
* InputModule
*/
/**
* @brief
*/
struct InputConfigData {
bool enabled = true;
bool rawMouseInput = false;
float mouseSensitivity = 1.0f;
bool invertMouseY = false;
bool invertMouseX = false;
float deadzone = 0.15f;
float triggerThreshold = 0.5f;
bool enableVibration = true;
int maxGamepads = 4;
bool autoConnectGamepads = true;
std::string gamepadMappingFile;
/**
* @brief
* @return 0-1 true
*/
bool isDeadzoneValid() const { return deadzone >= 0.0f && deadzone <= 1.0f; }
};
}

View File

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

View File

@ -1,129 +1,77 @@
#pragma once #pragma once
#include <extra2d/config/module_config.h> #include <extra2d/core/module.h>
#include <extra2d/config/module_initializer.h>
#include <extra2d/input/input_config.h>
#include <extra2d/platform/iinput.h> #include <extra2d/platform/iinput.h>
#include <extra2d/core/types.h> #include <extra2d/platform/window_module.h>
#include <typeindex>
namespace extra2d { namespace extra2d {
/** /**
* @file input_module.h
* @brief * @brief
* *
*
*
*/ */
class InputModule : public Module {
/**
* @brief
* IModuleConfig
*/
class InputModuleConfig : public IModuleConfig {
public: public:
InputConfigData inputConfig;
/** /**
* @brief * @brief
* @return
*/ */
ModuleInfo getModuleInfo() const override { struct Cfg {
ModuleInfo info; float deadzone;
info.id = 0; float mouseSensitivity;
info.name = "Input"; bool enableVibration;
info.version = "1.0.0"; int maxGamepads;
info.priority = ModulePriority::Input; int priority;
info.enabled = true;
return info; Cfg()
} : deadzone(0.15f)
, mouseSensitivity(1.0f)
, enableVibration(true)
, maxGamepads(4)
, priority(20)
{}
};
/** /**
* @brief * @brief
* @return * @param cfg
*/ */
std::string getConfigSectionName() const override { return "input"; } explicit InputModule(const Cfg& cfg = Cfg{});
/** /**
* @brief * @brief
* @return true
*/ */
bool validate() const override; ~InputModule() override;
/** bool init() override;
* @brief
* @param platform
*/
void applyPlatformConstraints(PlatformType platform) override;
/**
* @brief
*/
void resetToDefaults() override {
inputConfig = InputConfigData{};
}
/**
* @brief JSON
* @param jsonData JSON
* @return true
*/
bool loadFromJson(const void* jsonData) override;
/**
* @brief JSON
* @param jsonData JSON
* @return true
*/
bool saveToJson(void* jsonData) const override;
};
/**
* @brief
* IModuleInitializer
*/
class InputModuleInitializer : public IModuleInitializer {
public:
InputModuleInitializer();
~InputModuleInitializer() override;
ModuleId getModuleId() const override { return moduleId_; }
ModulePriority getPriority() const override { return ModulePriority::Input; }
std::vector<ModuleId> getDependencies() const override;
bool initialize(const IModuleConfig* config) override;
void shutdown() override; void shutdown() override;
bool isInitialized() const override { return initialized_; } bool ok() const override { return initialized_; }
const char* name() const override { return "input"; }
int priority() const override { return cfg_.priority; }
void setModuleId(ModuleId id) { moduleId_ = id; } /**
* @brief
* @return
*/
std::vector<std::type_index> deps() const override {
return {std::type_index(typeid(WindowModule))};
}
/** /**
* @brief * @brief
* @return * @return
*/ */
IInput* getInput() const { return input_; } IInput* input() const { return input_; }
/** /**
* @brief * @brief
*
*/ */
void update(); void update();
private: private:
ModuleId moduleId_ = INVALID_MODULE_ID; Cfg cfg_;
IInput* input_ = nullptr; IInput* input_ = nullptr;
bool initialized_ = false; bool initialized_ = false;
InputConfigData config_;
}; };
/** } // namespace extra2d
* @brief
* @return
*/
ModuleId get_input_module_id();
/**
* @brief
*/
void register_input_module();
}

View File

@ -1,72 +0,0 @@
#pragma once
#include <extra2d/config/module_config.h>
#include <extra2d/config/module_initializer.h>
#include <extra2d/config/platform_config.h>
namespace extra2d {
class PlatformModuleConfig : public IModuleConfig {
public:
PlatformType targetPlatform = PlatformType::Auto;
ModuleInfo getModuleInfo() const override {
ModuleInfo info;
info.id = 0;
info.name = "Platform";
info.version = "1.0.0";
info.priority = ModulePriority::Core;
info.enabled = true;
return info;
}
std::string getConfigSectionName() const override {
return "platform";
}
bool validate() const override {
return true;
}
void resetToDefaults() override {
targetPlatform = PlatformType::Auto;
}
bool loadFromJson(const void* jsonData) override;
bool saveToJson(void* jsonData) const override;
};
class PlatformModuleInitializer : public IModuleInitializer {
public:
PlatformModuleInitializer();
~PlatformModuleInitializer() override;
ModuleId getModuleId() const override { return moduleId_; }
ModulePriority getPriority() const override { return ModulePriority::Core; }
std::vector<ModuleId> getDependencies() const override { return {}; }
bool initialize(const IModuleConfig* config) override;
void shutdown() override;
bool isInitialized() const override { return initialized_; }
void setModuleId(ModuleId id) { moduleId_ = id; }
void setPlatform(PlatformType platform) { targetPlatform_ = platform; }
PlatformType getPlatform() const { return resolvedPlatform_; }
PlatformConfig* getPlatformConfig() const { return platformConfig_.get(); }
private:
bool initSwitch();
void shutdownSwitch();
ModuleId moduleId_ = INVALID_MODULE_ID;
bool initialized_ = false;
PlatformType targetPlatform_ = PlatformType::Auto;
PlatformType resolvedPlatform_ = PlatformType::Windows;
UniquePtr<PlatformConfig> platformConfig_;
};
ModuleId get_platform_module_id();
void register_platform_module();
} // namespace extra2d

View File

@ -1,195 +1,67 @@
#pragma once #pragma once
#include <extra2d/config/module_config.h> #include <extra2d/core/module.h>
#include <extra2d/config/module_initializer.h>
#include <extra2d/platform/window_config.h> #include <extra2d/platform/window_config.h>
#include <extra2d/platform/iwindow.h> #include <extra2d/platform/iwindow.h>
#include <string>
namespace extra2d { namespace extra2d {
/** /**
* @file window_module.h
* @brief * @brief
* *
* 使 SDL2
* - Windows
* - Linux
* - macOS
* - Nintendo Switch
*/ */
class WindowModule : public Module {
/**
* @brief
* IModuleConfig
*/
class WindowModuleConfig : public IModuleConfig {
public: public:
WindowConfigData windowConfig;
/** /**
* @brief * @brief
* @return
*/ */
ModuleInfo getModuleInfo() const override { struct Cfg {
ModuleInfo info; std::string title;
info.id = 0; int w;
info.name = "Window"; int h;
info.version = "1.0.0"; WindowMode mode;
info.priority = ModulePriority::Core; bool vsync;
info.enabled = true; int priority;
return info; std::string backend;
}
Cfg()
/** : title("Extra2D")
* @brief , w(1280)
* @return , h(720)
*/ , mode(WindowMode::Windowed)
std::string getConfigSectionName() const override { , vsync(true)
return "window"; , priority(0)
} , backend("sdl2") {}
};
/**
* @brief
* @return true
*/
bool validate() const override {
return windowConfig.width > 0 && windowConfig.height > 0;
}
/**
* @brief
* @param platform
*/
void applyPlatformConstraints(PlatformType platform) override;
/**
* @brief
*/
void resetToDefaults() override {
windowConfig = WindowConfigData{};
}
/**
* @brief JSON
* @param jsonData JSON
* @return true
*/
bool loadFromJson(const void* jsonData) override;
/**
* @brief JSON
* @param jsonData JSON
* @return true
*/
bool saveToJson(void* jsonData) const override;
};
/**
* @brief
* IModuleInitializer
*/
class WindowModuleInitializer : public IModuleInitializer {
public:
/** /**
* @brief * @brief
* @param cfg
*/ */
WindowModuleInitializer(); explicit WindowModule(const Cfg& cfg = Cfg());
/** /**
* @brief * @brief
*/ */
~WindowModuleInitializer() override; ~WindowModule() override;
/** bool init() override;
* @brief
* @return
*/
ModuleId getModuleId() const override { return moduleId_; }
/**
* @brief
* @return
*/
ModulePriority getPriority() const override { return ModulePriority::Core; }
/**
* @brief
* @return
*/
std::vector<ModuleId> getDependencies() const override { return {}; }
/**
* @brief
* @param config
* @return true
*/
bool initialize(const IModuleConfig* config) override;
/**
* @brief
*/
void shutdown() override; void shutdown() override;
bool ok() const override { return initialized_; }
const char* name() const override { return "window"; }
int priority() const override { return cfg_.priority; }
/** /**
* @brief * @brief
* @return true * @return
*/ */
bool isInitialized() const override { return initialized_; } IWindow* win() const { return win_.get(); }
/**
* @brief
* @param id
*/
void setModuleId(ModuleId id) { moduleId_ = id; }
/**
* @brief
* @param config
*/
void setWindowConfig(const WindowConfigData& config) { windowConfig_ = config; }
/**
* @brief
* @return
*/
IWindow* getWindow() const { return window_.get(); }
private: private:
/** Cfg cfg_;
* @brief SDL2 UniquePtr<IWindow> win_;
* @return true
*/
bool initSDL2();
/**
* @brief SDL2
*/
void shutdownSDL2();
/**
* @brief
* @param config
* @return true
*/
bool createWindow(const WindowConfigData& config);
ModuleId moduleId_ = INVALID_MODULE_ID;
bool initialized_ = false; bool initialized_ = false;
bool sdl2Initialized_ = false; bool sdlInited_ = false;
WindowConfigData windowConfig_;
UniquePtr<IWindow> window_;
}; };
/** } // namespace extra2d
* @brief
* @return
*/
ModuleId get_window_module_id();
/**
* @brief
*/
void register_window_module();
}

View File

@ -1,52 +0,0 @@
#pragma once
#include <string>
#include <vector>
namespace extra2d {
/**
* @file resource_config.h
* @brief
*
* ResourceModule
*/
/**
* @brief
*/
struct ResourceConfigData {
std::string assetRootPath = "assets";
std::string cachePath = "cache";
std::string savePath = "saves";
std::string configPath = "config";
std::string logPath = "logs";
bool useAssetCache = true;
int maxCacheSize = 512;
bool hotReloadEnabled = false;
float hotReloadInterval = 1.0f;
bool compressTextures = false;
bool preloadCommonAssets = true;
std::vector<std::string> searchPaths;
/**
* @brief
* @param path
*/
void addSearchPath(const std::string& path);
/**
* @brief
* @param path
*/
void removeSearchPath(const std::string& path);
/**
* @brief
* @param path
* @return true
*/
bool hasSearchPath(const std::string& path) const;
};
}

View File

@ -4,7 +4,7 @@
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/event/event_dispatcher.h> #include <extra2d/event/event_dispatcher.h>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/graphics/camera.h> #include <extra2d/graphics/camera/camera.h>
#include <extra2d/scene/node.h> #include <extra2d/scene/node.h>
#include <vector> #include <vector>

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/texture/texture.h>
#include <extra2d/scene/node.h> #include <extra2d/scene/node.h>
namespace extra2d { namespace extra2d {

View File

@ -26,6 +26,7 @@ public:
protected: protected:
void onTransitionStart() override; void onTransitionStart() override;
void renderContent(RenderBackend &renderer) override; void renderContent(RenderBackend &renderer) override;
void updateTransition(float dt);
private: private:
int divisions_; int divisions_;

View File

@ -34,6 +34,12 @@ protected:
*/ */
void onTransitionStart() override; void onTransitionStart() override;
/**
* @brief
* @param dt
*/
void updateTransition(float dt) override;
/** /**
* @brief * @brief
* *

View File

@ -29,6 +29,7 @@ public:
protected: protected:
void onTransitionStart() override; void onTransitionStart() override;
void renderContent(RenderBackend &renderer) override; void renderContent(RenderBackend &renderer) override;
void updateTransition(float dt);
private: private:
Axis axis_; Axis axis_;

View File

@ -24,6 +24,7 @@ public:
protected: protected:
void onTransitionStart() override; void onTransitionStart() override;
void renderContent(RenderBackend &renderer) override; void renderContent(RenderBackend &renderer) override;
void updateTransition(float dt);
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -80,11 +80,22 @@ public:
*/ */
bool isFinished() const { return isFinished_; } bool isFinished() const { return isFinished_; }
/**
* @brief
*/
bool isCancelled() const { return isCancelled_; }
/** /**
* @brief SceneManager * @brief SceneManager
*/ */
void finish(); void finish();
/**
* @brief
* @param immediate false则回滚到原场景
*/
void cancel(bool immediate = false);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 渲染 - 在 TransitionScene 上渲染新旧两个子场景 // 渲染 - 在 TransitionScene 上渲染新旧两个子场景
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -113,15 +124,25 @@ protected:
*/ */
virtual void drawInScene(RenderBackend &renderer); virtual void drawInScene(RenderBackend &renderer);
/**
* @brief
* @param dt
*/
virtual void updateTransition(float dt);
float duration_; float duration_;
float elapsed_ = 0.0f; float elapsed_ = 0.0f;
float progress_ = 0.0f; float progress_ = 0.0f;
bool isFinished_ = false; bool isFinished_ = false;
bool isCancelled_ = false;
Ptr<Scene> inScene_; // 要进入的场景 Ptr<Scene> inScene_; // 要进入的场景
Ptr<Scene> outScene_; // 要退出的场景 Ptr<Scene> outScene_; // 要退出的场景
FinishCallback finishCallback_; FinishCallback finishCallback_;
FinishCallback cancelCallback_; // 取消回调
friend class SceneManager;
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -27,6 +27,7 @@ public:
protected: protected:
void onTransitionStart() override; void onTransitionStart() override;
void renderContent(RenderBackend &renderer) override; void renderContent(RenderBackend &renderer) override;
void updateTransition(float dt);
private: private:
TransitionDirection direction_; TransitionDirection direction_;

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <extra2d/core/service_interface.h> #include <extra2d/core/service_interface.h>
#include <extra2d/graphics/camera.h> #include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/viewport_adapter.h> #include <extra2d/graphics/camera/viewport_adapter.h>
namespace extra2d { namespace extra2d {

View File

@ -0,0 +1,228 @@
#pragma once
#include <extra2d/core/service_interface.h>
#include <extra2d/core/types.h>
#include <cstdarg>
#include <string>
#include <type_traits>
namespace extra2d {
/**
* @brief
*/
enum class LogLevel {
Trace = 0,
Debug = 1,
Info = 2,
Warn = 3,
Error = 4,
Fatal = 5,
Off = 6
};
/**
* @brief
*/
class ILogger : public IService {
public:
virtual ~ILogger() = default;
/**
* @brief
*/
virtual void setLevel(LogLevel level) = 0;
/**
* @brief
*/
virtual LogLevel getLevel() const = 0;
/**
* @brief
*/
virtual bool isEnabled(LogLevel level) const = 0;
/**
* @brief
*/
virtual void log(LogLevel level, const char* fmt, ...) = 0;
/**
* @brief
*/
virtual void log(LogLevel level, const std::string& msg) = 0;
/**
* @brief Trace级别日志
*/
virtual void trace(const char* fmt, ...) = 0;
/**
* @brief Debug级别日志
*/
virtual void debug(const char* fmt, ...) = 0;
/**
* @brief Info级别日志
*/
virtual void info(const char* fmt, ...) = 0;
/**
* @brief Warn级别日志
*/
virtual void warn(const char* fmt, ...) = 0;
/**
* @brief Error级别日志
*/
virtual void error(const char* fmt, ...) = 0;
/**
* @brief Fatal级别日志
*/
virtual void fatal(const char* fmt, ...) = 0;
ServiceInfo getServiceInfo() const override {
ServiceInfo info;
info.name = "Logger";
info.priority = ServicePriority::Core;
info.enabled = true;
return info;
}
};
/**
* @brief
*/
class ConsoleLogger : public ILogger {
public:
ConsoleLogger();
~ConsoleLogger() override;
bool initialize() override;
void shutdown() override;
void setLevel(LogLevel level) override;
LogLevel getLevel() const override;
bool isEnabled(LogLevel level) const override;
void log(LogLevel level, const char* fmt, ...) override;
void log(LogLevel level, const std::string& msg) override;
void trace(const char* fmt, ...) override;
void debug(const char* fmt, ...) override;
void info(const char* fmt, ...) override;
void warn(const char* fmt, ...) override;
void error(const char* fmt, ...) override;
void fatal(const char* fmt, ...) override;
private:
void output(LogLevel level, const char* msg);
const char* getLevelString(LogLevel level);
LogLevel level_;
class Impl;
UniquePtr<Impl> impl_;
};
} // namespace extra2d
// 格式化辅助函数 - 将参数转换为字符串
namespace extra2d {
namespace detail {
template<typename T>
std::string to_string(T&& value) {
using Decayed = std::decay_t<T>;
if constexpr (std::is_same_v<Decayed, std::string>) {
return value;
} else if constexpr (std::is_same_v<Decayed, const char*>) {
return value ? value : "(null)";
} else if constexpr (std::is_arithmetic_v<Decayed>) {
if constexpr (std::is_same_v<Decayed, bool>) {
return value ? "true" : "false";
} else if constexpr (std::is_floating_point_v<Decayed>) {
return std::to_string(value);
} else {
return std::to_string(value);
}
} else {
return "<?>";
}
}
inline void format_impl(std::string& result, const char* fmt) {
result += fmt;
}
template<typename T, typename... Args>
void format_impl(std::string& result, const char* fmt, T&& value, Args&&... args) {
const char* p = fmt;
while (*p) {
if (*p == '{' && *(p + 1) == '}') {
result += to_string(std::forward<T>(value));
format_impl(result, p + 2, std::forward<Args>(args)...);
return;
}
result += *p++;
}
// 没有更多的 {},追加剩余参数(不应该发生)
result += " ";
result += to_string(std::forward<T>(value));
format_impl(result, p, std::forward<Args>(args)...);
}
}
template<typename... Args>
std::string format_str(const char* fmt, Args&&... args) {
if constexpr (sizeof...(args) == 0) {
return std::string(fmt);
} else {
std::string result;
detail::format_impl(result, fmt, std::forward<Args>(args)...);
return result;
}
}
}
// 便捷宏 - 自动获取日志服务
#define E2D_LOG(level, ...) \
do { \
if (auto logService = ::extra2d::ServiceLocator::instance().tryGetService<::extra2d::ILogger>()) { \
if (logService->isEnabled(level)) { \
logService->log(level, ::extra2d::format_str(__VA_ARGS__)); \
} \
} \
} while(0)
#define E2D_LOG_TRACE(...) \
E2D_LOG(::extra2d::LogLevel::Trace, __VA_ARGS__)
#define E2D_LOG_DEBUG(...) \
E2D_LOG(::extra2d::LogLevel::Debug, __VA_ARGS__)
#define E2D_LOG_INFO(...) \
E2D_LOG(::extra2d::LogLevel::Info, __VA_ARGS__)
#define E2D_LOG_WARN(...) \
E2D_LOG(::extra2d::LogLevel::Warn, __VA_ARGS__)
#define E2D_LOG_ERROR(...) \
E2D_LOG(::extra2d::LogLevel::Error, __VA_ARGS__)
#define E2D_LOG_FATAL(...) \
E2D_LOG(::extra2d::LogLevel::Fatal, __VA_ARGS__)
// 简写宏
#define E2D_INFO(...) E2D_LOG_INFO(__VA_ARGS__)
#define E2D_WARN(...) E2D_LOG_WARN(__VA_ARGS__)
#define E2D_ERROR(...) E2D_LOG_ERROR(__VA_ARGS__)
#define E2D_FATAL(...) E2D_LOG_FATAL(__VA_ARGS__)
#ifdef E2D_DEBUG
#define E2D_DEBUG_LOG(...) E2D_LOG_DEBUG(__VA_ARGS__)
#define E2D_TRACE(...) E2D_LOG_TRACE(__VA_ARGS__)
#else
#define E2D_DEBUG_LOG(...)
#define E2D_TRACE(...)
#endif

View File

@ -1,201 +1,12 @@
#pragma once #pragma once
#include <cstdio> /**
#include <extra2d/core/types.h> * @file logger.h
#include <sstream> * @brief
#include <string> *
#include <type_traits> * 便 logger_service.h
*/
namespace extra2d { #include <extra2d/services/logger_service.h>
#include <extra2d/core/service_locator.h>
enum class LogLevel {
Trace = 0,
Debug = 1,
Info = 2,
Warn = 3,
Error = 4,
Fatal = 5,
Off = 6
};
namespace detail {
template <typename T> inline std::string to_string_arg(const T &value) {
if constexpr (std::is_same_v<T, std::string>) {
return value;
} else if constexpr (std::is_same_v<T, const char *> ||
std::is_same_v<T, char *>) {
return value ? std::string(value) : std::string("(null)");
} else if constexpr (std::is_same_v<T, bool>) {
return value ? "true" : "false";
} else if constexpr (std::is_arithmetic_v<T>) {
if constexpr (std::is_floating_point_v<T>) {
char buf[64];
snprintf(buf, sizeof(buf), "%.2f", static_cast<double>(value));
return buf;
} else {
return std::to_string(value);
}
} else {
std::ostringstream oss;
oss << value;
return oss.str();
}
}
inline std::string format_impl(const char *fmt) {
std::string result;
while (*fmt) {
if (*fmt == '{' && *(fmt + 1) == '}') {
result += "{}";
fmt += 2;
} else {
result += *fmt;
++fmt;
}
}
return result;
}
template <typename T, typename... Args>
inline std::string format_impl(const char *fmt, const T &first,
const Args &...rest) {
std::string result;
while (*fmt) {
if (*fmt == '{') {
if (*(fmt + 1) == '}') {
result += to_string_arg(first);
fmt += 2;
result += format_impl(fmt, rest...);
return result;
} else if (*(fmt + 1) == ':') {
const char *end = fmt + 2;
while (*end && *end != '}')
++end;
if (*end == '}') {
std::string spec(fmt + 2, end);
if (spec.find('x') != std::string::npos ||
spec.find('X') != std::string::npos) {
if constexpr (std::is_integral_v<T>) {
char buf[32];
snprintf(buf, sizeof(buf), "0x%x",
static_cast<unsigned int>(first));
result += buf;
} else {
result += to_string_arg(first);
}
} else if (spec.find('f') != std::string::npos ||
spec.find('.') != std::string::npos) {
if constexpr (std::is_arithmetic_v<T>) {
int precision = 2;
auto dot = spec.find('.');
if (dot != std::string::npos) {
precision = 0;
for (size_t i = dot + 1;
i < spec.size() && spec[i] >= '0' && spec[i] <= '9'; ++i) {
precision = precision * 10 + (spec[i] - '0');
}
}
char fmtbuf[16];
snprintf(fmtbuf, sizeof(fmtbuf), "%%.%df", precision);
char buf[64];
snprintf(buf, sizeof(buf), fmtbuf, static_cast<double>(first));
result += buf;
} else {
result += to_string_arg(first);
}
} else {
result += to_string_arg(first);
}
fmt = end + 1;
result += format_impl(fmt, rest...);
return result;
}
}
}
result += *fmt;
++fmt;
}
return result;
}
} // namespace detail
template <typename... Args>
inline std::string e2d_format(const char *fmt, const Args &...args) {
return detail::format_impl(fmt, args...);
}
inline std::string e2d_format(const char *fmt) { return std::string(fmt); }
class Logger {
public:
static void init();
static void shutdown();
static void setLevel(LogLevel level);
static void setConsoleOutput(bool enable);
static void setFileOutput(const std::string &filename);
static LogLevel getLevel() { return level_; }
template <typename... Args>
static void log(LogLevel level, const char *fmt, const Args &...args) {
if (static_cast<int>(level) < static_cast<int>(level_))
return;
std::string msg = e2d_format(fmt, args...);
outputLog(level, msg.c_str());
}
static void log(LogLevel level, const char *msg) {
if (static_cast<int>(level) < static_cast<int>(level_))
return;
outputLog(level, msg);
}
private:
static void outputLog(LogLevel level, const char *msg);
static const char *getLevelString(LogLevel level);
static void writeToConsole(LogLevel level, const char *msg);
static void writeToFile(LogLevel level, const char *msg);
static LogLevel level_;
static bool initialized_;
static bool consoleOutput_;
static bool fileOutput_;
static std::string logFile_;
static void *logFileHandle_;
};
#ifdef E2D_DEBUG
#define E2D_LOG_TRACE(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Trace, __VA_ARGS__)
#define E2D_LOG_DEBUG(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Debug, __VA_ARGS__)
#else
#define E2D_LOG_TRACE(...)
#define E2D_LOG_DEBUG(...)
#endif
#define E2D_LOG_INFO(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Info, __VA_ARGS__)
#define E2D_LOG_WARN(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Warn, __VA_ARGS__)
#define E2D_LOG_ERROR(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Error, __VA_ARGS__)
#define E2D_LOG_FATAL(...) \
::extra2d::Logger::log(::extra2d::LogLevel::Fatal, __VA_ARGS__)
#define E2D_INFO(...) E2D_LOG_INFO(__VA_ARGS__)
#define E2D_WARN(...) E2D_LOG_WARN(__VA_ARGS__)
#define E2D_ERROR(...) E2D_LOG_ERROR(__VA_ARGS__)
#define E2D_FATAL(...) E2D_LOG_FATAL(__VA_ARGS__)
#ifdef E2D_DEBUG
#define E2D_DEBUG_LOG(...) E2D_LOG_DEBUG(__VA_ARGS__)
#define E2D_TRACE(...) E2D_LOG_TRACE(__VA_ARGS__)
#else
#define E2D_DEBUG_LOG(...)
#define E2D_TRACE(...)
#endif
} // namespace extra2d

View File

@ -1,66 +0,0 @@
#pragma once
#include <extra2d/config/module_config.h>
#include <extra2d/config/module_initializer.h>
#include <extra2d/utils/logger.h>
#include <string>
namespace extra2d {
class LoggerModuleConfig : public IModuleConfig {
public:
LogLevel logLevel = LogLevel::Info;
bool consoleOutput = true;
bool fileOutput = false;
std::string logFilePath;
ModuleInfo getModuleInfo() const override {
ModuleInfo info;
info.id = 0;
info.name = "Logger";
info.version = "1.0.0";
info.priority = ModulePriority::Core;
info.enabled = true;
return info;
}
std::string getConfigSectionName() const override {
return "logger";
}
bool validate() const override {
return true;
}
void resetToDefaults() override {
logLevel = LogLevel::Info;
consoleOutput = true;
fileOutput = false;
logFilePath.clear();
}
bool loadFromJson(const void* jsonData) override;
bool saveToJson(void* jsonData) const override;
};
class LoggerModuleInitializer : public IModuleInitializer {
public:
LoggerModuleInitializer();
~LoggerModuleInitializer() override;
ModuleId getModuleId() const override { return moduleId_; }
ModulePriority getPriority() const override { return ModulePriority::Core; }
std::vector<ModuleId> getDependencies() const override { return {}; }
bool initialize(const IModuleConfig* config) override;
void shutdown() override;
bool isInitialized() const override { return initialized_; }
void setModuleId(ModuleId id) { moduleId_ = id; }
private:
ModuleId moduleId_ = INVALID_MODULE_ID;
bool initialized_ = false;
};
} // namespace extra2d

View File

@ -1,18 +1,17 @@
#include <extra2d/app/application.h> #include <extra2d/app/application.h>
#include <extra2d/config/config_module.h> #include <extra2d/core/registry.h>
#include <extra2d/config/module_registry.h>
#include <extra2d/graphics/render_config.h>
#include <extra2d/graphics/render_module.h>
#include <extra2d/graphics/vram_manager.h>
#include <extra2d/platform/iinput.h>
#include <extra2d/platform/input_module.h>
#include <extra2d/platform/platform_init_module.h>
#include <extra2d/platform/window_module.h> #include <extra2d/platform/window_module.h>
#include <extra2d/services/camera_service.h> #include <extra2d/platform/input_module.h>
#include <extra2d/services/event_service.h> #include <extra2d/graphics/core/render_module.h>
#include <extra2d/platform/iwindow.h>
#include <extra2d/platform/iinput.h>
#include <extra2d/graphics/core/render_backend.h>
#include <extra2d/services/scene_service.h> #include <extra2d/services/scene_service.h>
#include <extra2d/services/timer_service.h> #include <extra2d/services/timer_service.h>
#include <extra2d/services/event_service.h>
#include <extra2d/services/camera_service.h>
#include <extra2d/services/logger_service.h>
#include <extra2d/graphics/memory/vram_manager.h>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
@ -21,391 +20,249 @@ namespace extra2d {
static double getTimeSeconds() { static double getTimeSeconds() {
#ifdef __SWITCH__ #ifdef __SWITCH__
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
return static_cast<double>(ts.tv_sec) + return static_cast<double>(ts.tv_sec) +
static_cast<double>(ts.tv_nsec) / 1000000000.0; static_cast<double>(ts.tv_nsec) / 1000000000.0;
#else #else
using namespace std::chrono; using namespace std::chrono;
auto now = steady_clock::now(); auto now = steady_clock::now();
auto duration = now.time_since_epoch(); auto duration = now.time_since_epoch();
return duration_cast<std::chrono::duration<double>>(duration).count(); return duration_cast<std::chrono::duration<double>>(duration).count();
#endif #endif
} }
Application &Application::get() { Application& Application::get() {
static Application instance; static Application instance;
return instance; return instance;
} }
bool Application::init() { Application::Application() {
AppConfig cfg; Registry::instance().setApp(this);
return init(cfg);
}
bool Application::init(const AppConfig &config) {
if (initialized_) {
return true;
}
register_config_module();
register_platform_module();
register_window_module();
register_input_module();
register_render_module();
auto *configInit =
ModuleRegistry::instance().getInitializer(get_config_module_id());
if (configInit) {
auto *cfgInit = dynamic_cast<ConfigModuleInitializer *>(configInit);
if (cfgInit) {
cfgInit->setAppConfig(config);
}
}
return initModules();
}
bool Application::init(const std::string &configPath) {
if (initialized_) {
return true;
}
register_config_module();
register_platform_module();
register_window_module();
register_input_module();
register_render_module();
auto *configInit =
ModuleRegistry::instance().getInitializer(get_config_module_id());
if (configInit) {
auto *cfgInit = dynamic_cast<ConfigModuleInitializer *>(configInit);
if (cfgInit) {
cfgInit->setConfigPath(configPath);
}
}
return initModules();
}
void Application::registerCoreServices() {
auto &locator = ServiceLocator::instance();
if (!locator.hasService<ISceneService>()) {
locator.registerService<ISceneService>(makeShared<SceneService>());
}
if (!locator.hasService<ITimerService>()) {
locator.registerService<ITimerService>(makeShared<TimerService>());
}
if (!locator.hasService<ICameraService>()) {
auto cameraService = makeShared<CameraService>();
if (window_) {
cameraService->setViewport(0, static_cast<float>(window_->width()),
static_cast<float>(window_->height()), 0);
ViewportConfig vpConfig;
vpConfig.logicWidth = static_cast<float>(window_->width());
vpConfig.logicHeight = static_cast<float>(window_->height());
vpConfig.mode = ViewportMode::AspectRatio;
cameraService->setViewportConfig(vpConfig);
cameraService->updateViewport(window_->width(), window_->height());
}
locator.registerService<ICameraService>(cameraService);
}
}
bool Application::initModules() {
auto &locator = ServiceLocator::instance();
if (!locator.hasService<IEventService>()) {
locator.registerService<IEventService>(makeShared<EventService>());
}
auto initOrder = ModuleRegistry::instance().getInitializationOrder();
for (ModuleId moduleId : initOrder) {
auto *initializer = ModuleRegistry::instance().getInitializer(moduleId);
if (!initializer) {
continue;
}
auto *moduleConfig = ModuleRegistry::instance().getModuleConfig(moduleId);
if (!moduleConfig) {
continue;
}
auto info = moduleConfig->getModuleInfo();
if (!info.enabled) {
continue;
}
if (info.name == "Render") {
continue;
}
if (!initializer->initialize(moduleConfig)) {
return false;
}
}
auto *windowInit =
ModuleRegistry::instance().getInitializer(get_window_module_id());
if (!windowInit || !windowInit->isInitialized()) {
return false;
}
auto *windowModule = dynamic_cast<WindowModuleInitializer *>(windowInit);
if (!windowModule) {
return false;
}
window_ = windowModule->getWindow();
if (!window_) {
return false;
}
auto *renderInit =
ModuleRegistry::instance().getInitializer(get_render_module_id());
if (renderInit) {
auto *renderModule = dynamic_cast<RenderModuleInitializer *>(renderInit);
if (renderModule) {
renderModule->setWindow(window_);
auto *renderConfig =
ModuleRegistry::instance().getModuleConfig(get_render_module_id());
if (renderConfig && !renderInit->initialize(renderConfig)) {
return false;
}
}
}
registerCoreServices();
if (!ServiceLocator::instance().initializeAll()) {
return false;
}
auto cameraService = ServiceLocator::instance().getService<ICameraService>();
if (cameraService && window_) {
window_->onResize([cameraService](int width, int height) {
cameraService->updateViewport(width, height);
cameraService->applyViewportAdapter();
auto sceneService =
ServiceLocator::instance().getService<ISceneService>();
if (sceneService) {
auto currentScene = sceneService->getCurrentScene();
if (currentScene) {
currentScene->setViewportSize(static_cast<float>(width),
static_cast<float>(height));
}
}
});
}
initialized_ = true;
running_ = true;
return true;
}
void Application::shutdown() {
if (!initialized_)
return;
VRAMMgr::get().printStats();
ServiceLocator::instance().clear();
window_ = nullptr;
auto initOrder = ModuleRegistry::instance().getInitializationOrder();
for (auto it = initOrder.rbegin(); it != initOrder.rend(); ++it) {
ModuleId moduleId = *it;
auto *initializer = ModuleRegistry::instance().getInitializer(moduleId);
if (initializer && initializer->isInitialized()) {
initializer->shutdown();
}
}
initialized_ = false;
running_ = false;
} }
Application::~Application() { Application::~Application() {
if (initialized_) { if (initialized_) {
shutdown(); shutdown();
} }
}
bool Application::init() {
AppConfig cfg;
return init(cfg);
}
bool Application::init(const AppConfig& config) {
if (initialized_) {
return true;
}
appConfig_ = config;
// 首先注册日志服务(模块初始化可能需要它)
auto& locator = ServiceLocator::instance();
if (!locator.hasService<ILogger>()) {
auto logger = makeShared<ConsoleLogger>();
locator.registerService(std::static_pointer_cast<ILogger>(logger));
}
// 初始化所有模块(拓扑排序)
if (!Registry::instance().init()) {
return false;
}
// 模块初始化完成后,注册其他核心服务
registerCoreServices();
initialized_ = true;
running_ = true;
return true;
}
void Application::registerCoreServices() {
auto& locator = ServiceLocator::instance();
if (!locator.hasService<ISceneService>()) {
auto service = makeShared<SceneService>();
locator.registerService(std::static_pointer_cast<ISceneService>(service));
}
if (!locator.hasService<ITimerService>()) {
auto service = makeShared<TimerService>();
locator.registerService(std::static_pointer_cast<ITimerService>(service));
}
if (!locator.hasService<IEventService>()) {
auto service = makeShared<EventService>();
locator.registerService(std::static_pointer_cast<IEventService>(service));
}
auto* winMod = get<WindowModule>();
if (winMod && winMod->win() && !locator.hasService<ICameraService>()) {
auto cameraService = makeShared<CameraService>();
auto* win = winMod->win();
cameraService->setViewport(0, static_cast<float>(win->width()),
static_cast<float>(win->height()), 0);
ViewportConfig vpConfig;
vpConfig.logicWidth = static_cast<float>(win->width());
vpConfig.logicHeight = static_cast<float>(win->height());
vpConfig.mode = ViewportMode::AspectRatio;
cameraService->setViewportConfig(vpConfig);
cameraService->updateViewport(win->width(), win->height());
locator.registerService(std::static_pointer_cast<ICameraService>(cameraService));
win->onResize([cameraService](int width, int height) {
cameraService->updateViewport(width, height);
cameraService->applyViewportAdapter();
});
}
locator.initializeAll();
}
void Application::shutdown() {
if (!initialized_) return;
VRAMMgr::get().printStats();
ServiceLocator::instance().clear();
Registry::instance().shutdown();
Registry::instance().clear();
initialized_ = false;
running_ = false;
} }
void Application::run() { void Application::run() {
if (!initialized_) { if (!initialized_) return;
return;
} auto* winMod = get<WindowModule>();
if (!winMod || !winMod->win()) return;
lastFrameTime_ = getTimeSeconds();
lastFrameTime_ = getTimeSeconds();
while (running_ && !window_->shouldClose()) {
mainLoop(); while (running_ && !winMod->win()->shouldClose()) {
} mainLoop();
}
} }
void Application::quit() { void Application::quit() {
shouldQuit_ = true; shouldQuit_ = true;
running_ = false; running_ = false;
} }
void Application::pause() { void Application::pause() {
if (!paused_) { if (!paused_) {
paused_ = true; paused_ = true;
ServiceLocator::instance().pauseAll(); ServiceLocator::instance().pauseAll();
} }
} }
void Application::resume() { void Application::resume() {
if (paused_) { if (paused_) {
paused_ = false; paused_ = false;
ServiceLocator::instance().resumeAll(); ServiceLocator::instance().resumeAll();
lastFrameTime_ = getTimeSeconds(); lastFrameTime_ = getTimeSeconds();
} }
} }
void Application::mainLoop() { void Application::mainLoop() {
double currentTime = getTimeSeconds(); double currentTime = getTimeSeconds();
deltaTime_ = static_cast<float>(currentTime - lastFrameTime_); deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
lastFrameTime_ = currentTime; lastFrameTime_ = currentTime;
totalTime_ += deltaTime_; totalTime_ += deltaTime_;
frameCount_++; frameCount_++;
fpsTimer_ += deltaTime_; fpsTimer_ += deltaTime_;
if (fpsTimer_ >= 1.0f) { if (fpsTimer_ >= 1.0f) {
currentFps_ = frameCount_; currentFps_ = frameCount_;
frameCount_ = 0; frameCount_ = 0;
fpsTimer_ -= 1.0f; fpsTimer_ -= 1.0f;
} }
window_->poll(); auto* winMod = get<WindowModule>();
if (winMod && winMod->win()) {
auto eventService = ServiceLocator::instance().getService<IEventService>(); winMod->win()->poll();
if (eventService) { }
eventService->processQueue();
} auto eventService = ServiceLocator::instance().getService<IEventService>();
if (eventService) {
if (!paused_) { eventService->processQueue();
update(); }
}
if (!paused_) {
render(); update();
}
const auto &appConfig = ConfigManager::instance().appConfig();
render();
auto *renderConfig =
ModuleRegistry::instance().getModuleConfig(get_render_module_id()); // 帧率限制
auto *renderModuleConfig = auto* renderMod = get<RenderModule>();
dynamic_cast<const RenderModuleConfig *>(renderConfig); if (renderMod && renderMod->renderer()) {
// 这里可以添加帧率限制逻辑
if (renderModuleConfig && !renderModuleConfig->vsync &&
renderModuleConfig->targetFPS > 0) {
double frameEndTime = getTimeSeconds();
double frameTime = frameEndTime - currentTime;
double target = 1.0 / static_cast<double>(renderModuleConfig->targetFPS);
if (frameTime < target) {
auto sleepSeconds = target - frameTime;
std::this_thread::sleep_for(std::chrono::duration<double>(sleepSeconds));
} }
}
ConfigManager::instance().update(deltaTime_);
} }
void Application::update() { ServiceLocator::instance().updateAll(deltaTime_); } void Application::update() {
ServiceLocator::instance().updateAll(deltaTime_);
auto* inputMod = get<InputModule>();
if (inputMod) {
inputMod->update();
}
}
void Application::render() { void Application::render() {
auto *renderInit = auto* renderMod = get<RenderModule>();
ModuleRegistry::instance().getInitializer(get_render_module_id()); if (!renderMod || !renderMod->renderer()) return;
RenderBackend *renderer = nullptr;
if (renderInit) { auto* renderer = renderMod->renderer();
auto *renderModule = dynamic_cast<RenderModuleInitializer *>(renderInit); auto* winMod = get<WindowModule>();
if (renderModule) { if (!winMod || !winMod->win()) return;
renderer = renderModule->getRenderer();
auto cameraService = ServiceLocator::instance().getService<ICameraService>();
if (cameraService) {
const auto& vp = cameraService->getViewportResult().viewport;
renderer->setViewport(
static_cast<int>(vp.origin.x), static_cast<int>(vp.origin.y),
static_cast<int>(vp.size.width), static_cast<int>(vp.size.height));
renderer->setViewProjection(cameraService->getViewProjectionMatrix());
} else {
renderer->setViewport(0, 0, winMod->win()->width(), winMod->win()->height());
} }
}
auto sceneService = ServiceLocator::instance().getService<ISceneService>();
if (!renderer) { if (sceneService) {
return; sceneService->render(*renderer);
}
auto cameraService = ServiceLocator::instance().getService<ICameraService>();
if (cameraService) {
const auto &vp = cameraService->getViewportResult().viewport;
renderer->setViewport(
static_cast<int>(vp.origin.x), static_cast<int>(vp.origin.y),
static_cast<int>(vp.size.width), static_cast<int>(vp.size.height));
renderer->setViewProjection(cameraService->getViewProjectionMatrix());
} else {
renderer->setViewport(0, 0, window_->width(), window_->height());
}
auto sceneService = ServiceLocator::instance().getService<ISceneService>();
if (sceneService) {
sceneService->render(*renderer);
}
window_->swap();
}
IInput &Application::input() { return *window_->input(); }
RenderBackend &Application::renderer() {
auto *renderInit =
ModuleRegistry::instance().getInitializer(get_render_module_id());
if (renderInit) {
auto *renderModule = dynamic_cast<RenderModuleInitializer *>(renderInit);
if (renderModule && renderModule->getRenderer()) {
return *renderModule->getRenderer();
} }
}
static RenderBackend *dummy = nullptr; winMod->win()->swap();
if (!dummy) {
dummy = RenderBackend::create(BackendType::OpenGL).release();
}
return *dummy;
} }
SharedPtr<ISceneService> Application::scenes() { IWindow* Application::window() {
return ServiceLocator::instance().getService<ISceneService>(); auto* winMod = get<WindowModule>();
return winMod ? winMod->win() : nullptr;
} }
SharedPtr<ITimerService> Application::timers() { RenderBackend* Application::renderer() {
return ServiceLocator::instance().getService<ITimerService>(); auto* renderMod = get<RenderModule>();
return renderMod ? renderMod->renderer() : nullptr;
} }
SharedPtr<IEventService> Application::events() { IInput* Application::input() {
return ServiceLocator::instance().getService<IEventService>(); auto* winMod = get<WindowModule>();
} return (winMod && winMod->win()) ? winMod->win()->input() : nullptr;
SharedPtr<ICameraService> Application::camera() {
return ServiceLocator::instance().getService<ICameraService>();
} }
void Application::enterScene(Ptr<Scene> scene) { void Application::enterScene(Ptr<Scene> scene) {
auto sceneService = ServiceLocator::instance().getService<ISceneService>(); auto sceneService = ServiceLocator::instance().getService<ISceneService>();
if (sceneService && scene) { auto* winMod = get<WindowModule>();
scene->setViewportSize(static_cast<float>(window_->width()), if (sceneService && scene && winMod && winMod->win()) {
static_cast<float>(window_->height())); scene->setViewportSize(static_cast<float>(winMod->win()->width()),
sceneService->enterScene(scene); static_cast<float>(winMod->win()->height()));
} sceneService->enterScene(scene);
} }
ConfigManager &Application::config() { return ConfigManager::instance(); }
const AppConfig &Application::getConfig() const {
return ConfigManager::instance().appConfig();
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,341 +0,0 @@
#include <extra2d/config/config_loader.h>
#include <extra2d/config/config_manager.h>
#include <extra2d/config/module_registry.h>
#include <extra2d/utils/logger.h>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cctype>
#include <map>
namespace extra2d {
/**
* @brief
* @param str
* @return
*/
static std::string trim(const std::string& str) {
size_t start = 0;
while (start < str.length() && std::isspace(static_cast<unsigned char>(str[start]))) {
++start;
}
size_t end = str.length();
while (end > start && std::isspace(static_cast<unsigned char>(str[end - 1]))) {
--end;
}
return str.substr(start, end - start);
}
/**
* @brief
* @param str
* @return
*/
static std::string toLower(const std::string& str) {
std::string result = str;
for (char& c : result) {
c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
}
return result;
}
/**
* @brief INI
*/
using IniData = std::map<std::string, std::map<std::string, std::string>>;
/**
* @brief INI
* @param content INI
* @param data INI
* @return
*/
static ConfigLoadResult parseIniContent(const std::string& content, IniData& data) {
std::istringstream stream(content);
std::string line;
std::string currentSection;
int lineNumber = 0;
while (std::getline(stream, line)) {
++lineNumber;
line = trim(line);
if (line.empty() || line[0] == ';' || line[0] == '#') {
continue;
}
if (line[0] == '[') {
size_t endBracket = line.find(']');
if (endBracket == std::string::npos) {
return ConfigLoadResult::error("INI 解析错误: 缺少右括号", lineNumber);
}
currentSection = trim(line.substr(1, endBracket - 1));
if (data.find(currentSection) == data.end()) {
data[currentSection] = std::map<std::string, std::string>();
}
} else {
size_t equalPos = line.find('=');
if (equalPos == std::string::npos) {
continue;
}
std::string key = trim(line.substr(0, equalPos));
std::string value = trim(line.substr(equalPos + 1));
if (currentSection.empty()) {
return ConfigLoadResult::error("INI 解析错误: 键值对不在任何节中", lineNumber);
}
data[currentSection][key] = value;
}
}
return ConfigLoadResult::ok();
}
/**
* @brief INI
* @param data INI
* @param section
* @param key
* @param defaultValue
* @return
*/
static std::string getIniValue(const IniData& data, const std::string& section,
const std::string& key, const std::string& defaultValue = "") {
auto sectionIt = data.find(section);
if (sectionIt == data.end()) {
return defaultValue;
}
auto keyIt = sectionIt->second.find(key);
if (keyIt == sectionIt->second.end()) {
return defaultValue;
}
return keyIt->second;
}
/**
* @brief INI
* @param data INI
* @param section
* @param key
* @return
*/
static bool hasIniValue(const IniData& data, const std::string& section, const std::string& key) {
auto sectionIt = data.find(section);
if (sectionIt == data.end()) {
return false;
}
return sectionIt->second.find(key) != sectionIt->second.end();
}
ConfigLoadResult IniConfigLoader::load(const std::string& filepath, AppConfig& config) {
E2D_LOG_INFO("正在从 INI 文件加载应用配置: {}", filepath);
std::ifstream file(filepath);
if (!file.is_open()) {
E2D_LOG_ERROR("无法打开配置文件: {}", filepath);
return ConfigLoadResult::error("无法打开配置文件: " + filepath);
}
std::stringstream buffer;
buffer << file.rdbuf();
std::string content = buffer.str();
file.close();
return loadFromString(content, config);
}
ConfigSaveResult IniConfigLoader::save(const std::string& filepath, const AppConfig& config) {
E2D_LOG_INFO("正在保存应用配置到 INI 文件: {}", filepath);
std::string content = saveToString(config);
std::ofstream file(filepath);
if (!file.is_open()) {
E2D_LOG_ERROR("无法创建配置文件: {}", filepath);
return ConfigSaveResult::error("无法创建配置文件: " + filepath);
}
file << content;
file.close();
E2D_LOG_INFO("配置已成功保存到: {}", filepath);
return ConfigSaveResult::ok();
}
ConfigLoadResult IniConfigLoader::loadFromString(const std::string& content, AppConfig& config) {
IniData data;
auto result = parseIniContent(content, data);
if (result.hasError()) {
return result;
}
if (hasIniValue(data, "app", "name")) {
config.appName = getIniValue(data, "app", "name");
}
if (hasIniValue(data, "app", "version")) {
config.appVersion = getIniValue(data, "app", "version");
}
if (hasIniValue(data, "app", "organization")) {
config.organization = getIniValue(data, "app", "organization");
}
if (hasIniValue(data, "app", "configFile")) {
config.configFile = getIniValue(data, "app", "configFile");
}
if (hasIniValue(data, "app", "targetPlatform")) {
int value;
auto res = parseInt(getIniValue(data, "app", "targetPlatform"), value, "app.targetPlatform");
if (res.isOk()) {
config.targetPlatform = static_cast<PlatformType>(value);
}
}
E2D_LOG_INFO("INI 应用配置加载成功");
return ConfigLoadResult::ok();
}
std::string IniConfigLoader::saveToString(const AppConfig& config) {
std::ostringstream oss;
oss << "[app]\n";
oss << "name=" << config.appName << "\n";
oss << "version=" << config.appVersion << "\n";
oss << "organization=" << config.organization << "\n";
oss << "configFile=" << config.configFile << "\n";
oss << "targetPlatform=" << static_cast<int>(config.targetPlatform) << "\n";
return oss.str();
}
ConfigLoadResult IniConfigLoader::loadWithModules(const std::string& filepath) {
E2D_LOG_INFO("正在从 INI 文件加载完整配置(含模块): {}", filepath);
std::ifstream file(filepath);
if (!file.is_open()) {
E2D_LOG_ERROR("无法打开配置文件: {}", filepath);
return ConfigLoadResult::error("无法打开配置文件: " + filepath);
}
std::stringstream buffer;
buffer << file.rdbuf();
std::string content = buffer.str();
file.close();
IniData data;
auto result = parseIniContent(content, data);
if (result.hasError()) {
return result;
}
auto& registry = ModuleRegistry::instance();
auto moduleIds = registry.getAllModules();
for (ModuleId moduleId : moduleIds) {
IModuleConfig* moduleConfig = registry.getModuleConfig(moduleId);
if (!moduleConfig) continue;
std::string sectionName = moduleConfig->getConfigSectionName();
if (sectionName.empty()) continue;
if (data.find(sectionName) != data.end()) {
E2D_LOG_DEBUG("加载模块 {} 的 INI 配置", moduleConfig->getModuleInfo().name);
}
}
E2D_LOG_INFO("完整配置加载成功");
return ConfigLoadResult::ok();
}
ConfigSaveResult IniConfigLoader::saveWithModules(const std::string& filepath) {
E2D_LOG_INFO("正在保存完整配置(含模块)到 INI 文件: {}", filepath);
std::ostringstream oss;
oss << saveToString(ConfigManager::instance().appConfig());
auto& registry = ModuleRegistry::instance();
auto moduleIds = registry.getAllModules();
for (ModuleId moduleId : moduleIds) {
IModuleConfig* moduleConfig = registry.getModuleConfig(moduleId);
if (!moduleConfig) continue;
std::string sectionName = moduleConfig->getConfigSectionName();
if (sectionName.empty()) continue;
oss << "\n[" << sectionName << "]\n";
}
std::ofstream file(filepath);
if (!file.is_open()) {
E2D_LOG_ERROR("无法创建配置文件: {}", filepath);
return ConfigSaveResult::error("无法创建配置文件: " + filepath);
}
file << oss.str();
file.close();
E2D_LOG_INFO("完整配置已成功保存到: {}", filepath);
return ConfigSaveResult::ok();
}
bool IniConfigLoader::supportsFile(const std::string& filepath) const {
if (filepath.length() >= 4) {
std::string ext = filepath.substr(filepath.length() - 4);
for (char& c : ext) c = static_cast<char>(std::tolower(c));
return ext == ".ini";
}
return false;
}
UniquePtr<ConfigLoader> IniConfigLoader::clone() const {
return makeUnique<IniConfigLoader>();
}
std::string IniConfigLoader::sectionKey(const std::string& section, const std::string& key) const {
return section + "." + key;
}
ConfigLoadResult IniConfigLoader::parseInt(const std::string& value, int& result, const std::string& fieldName) {
try {
size_t pos;
result = std::stoi(value, &pos);
if (pos != value.length()) {
return ConfigLoadResult::error("无法解析整数值: " + value, -1, fieldName);
}
return ConfigLoadResult::ok();
} catch (const std::exception& e) {
return ConfigLoadResult::error(std::string("解析整数失败: ") + e.what(), -1, fieldName);
}
}
ConfigLoadResult IniConfigLoader::parseFloat(const std::string& value, float& result, const std::string& fieldName) {
try {
size_t pos;
result = std::stof(value, &pos);
if (pos != value.length()) {
return ConfigLoadResult::error("无法解析浮点数值: " + value, -1, fieldName);
}
return ConfigLoadResult::ok();
} catch (const std::exception& e) {
return ConfigLoadResult::error(std::string("解析浮点数失败: ") + e.what(), -1, fieldName);
}
}
ConfigLoadResult IniConfigLoader::parseBool(const std::string& value, bool& result, const std::string& fieldName) {
std::string lower = toLower(value);
if (lower == "true" || lower == "1" || lower == "yes" || lower == "on") {
result = true;
return ConfigLoadResult::ok();
}
if (lower == "false" || lower == "0" || lower == "no" || lower == "off") {
result = false;
return ConfigLoadResult::ok();
}
return ConfigLoadResult::error("无法解析布尔值: " + value, -1, fieldName);
}
}

View File

@ -1,188 +0,0 @@
#include <extra2d/config/config_loader.h>
#include <extra2d/config/module_registry.h>
#include <extra2d/utils/logger.h>
#include <nlohmann/json.hpp>
#include <fstream>
#include <sstream>
using json = nlohmann::json;
namespace extra2d {
ConfigLoadResult JsonConfigLoader::load(const std::string& filepath, AppConfig& config) {
E2D_LOG_INFO("正在从 JSON 文件加载应用配置: {}", filepath);
std::ifstream file(filepath);
if (!file.is_open()) {
E2D_LOG_ERROR("无法打开配置文件: {}", filepath);
return ConfigLoadResult::error("无法打开配置文件: " + filepath);
}
std::stringstream buffer;
buffer << file.rdbuf();
std::string content = buffer.str();
file.close();
return loadFromString(content, config);
}
ConfigSaveResult JsonConfigLoader::save(const std::string& filepath, const AppConfig& config) {
E2D_LOG_INFO("正在保存应用配置到 JSON 文件: {}", filepath);
std::string content = saveToString(config);
std::ofstream file(filepath);
if (!file.is_open()) {
E2D_LOG_ERROR("无法创建配置文件: {}", filepath);
return ConfigSaveResult::error("无法创建配置文件: " + filepath);
}
file << content;
file.close();
E2D_LOG_INFO("配置已成功保存到: {}", filepath);
return ConfigSaveResult::ok();
}
ConfigLoadResult JsonConfigLoader::loadFromString(const std::string& content, AppConfig& config) {
json root;
try {
root = json::parse(content);
} catch (const json::parse_error& e) {
E2D_LOG_ERROR("JSON 解析错误: {}", e.what());
return ConfigLoadResult::error(std::string("JSON 解析错误: ") + e.what(),
static_cast<int>(e.byte));
}
if (root.contains("appName") && root["appName"].is_string()) {
config.appName = root["appName"].get<std::string>();
}
if (root.contains("appVersion") && root["appVersion"].is_string()) {
config.appVersion = root["appVersion"].get<std::string>();
}
if (root.contains("organization") && root["organization"].is_string()) {
config.organization = root["organization"].get<std::string>();
}
if (root.contains("configFile") && root["configFile"].is_string()) {
config.configFile = root["configFile"].get<std::string>();
}
if (root.contains("targetPlatform") && root["targetPlatform"].is_number_integer()) {
config.targetPlatform = static_cast<PlatformType>(root["targetPlatform"].get<int>());
}
E2D_LOG_INFO("JSON 应用配置加载成功");
return ConfigLoadResult::ok();
}
std::string JsonConfigLoader::saveToString(const AppConfig& config) {
json root;
root["appName"] = config.appName;
root["appVersion"] = config.appVersion;
root["organization"] = config.organization;
root["configFile"] = config.configFile;
root["targetPlatform"] = static_cast<int>(config.targetPlatform);
return root.dump(4);
}
ConfigLoadResult JsonConfigLoader::loadWithModules(const std::string& filepath) {
E2D_LOG_INFO("正在从 JSON 文件加载完整配置(含模块): {}", filepath);
std::ifstream file(filepath);
if (!file.is_open()) {
E2D_LOG_ERROR("无法打开配置文件: {}", filepath);
return ConfigLoadResult::error("无法打开配置文件: " + filepath);
}
std::stringstream buffer;
buffer << file.rdbuf();
std::string content = buffer.str();
file.close();
json root;
try {
root = json::parse(content);
} catch (const json::parse_error& e) {
E2D_LOG_ERROR("JSON 解析错误: {}", e.what());
return ConfigLoadResult::error(std::string("JSON 解析错误: ") + e.what(),
static_cast<int>(e.byte));
}
auto& registry = ModuleRegistry::instance();
auto moduleIds = registry.getAllModules();
for (ModuleId moduleId : moduleIds) {
IModuleConfig* moduleConfig = registry.getModuleConfig(moduleId);
if (!moduleConfig) continue;
std::string sectionName = moduleConfig->getConfigSectionName();
if (sectionName.empty()) continue;
if (root.contains(sectionName)) {
if (!moduleConfig->loadFromJson(&root[sectionName])) {
E2D_LOG_WARN("模块 {} 配置加载失败", moduleConfig->getModuleInfo().name);
} else {
E2D_LOG_DEBUG("模块 {} 配置加载成功", moduleConfig->getModuleInfo().name);
}
}
}
E2D_LOG_INFO("完整配置加载成功");
return ConfigLoadResult::ok();
}
ConfigSaveResult JsonConfigLoader::saveWithModules(const std::string& filepath) {
E2D_LOG_INFO("正在保存完整配置(含模块)到 JSON 文件: {}", filepath);
json root;
auto& registry = ModuleRegistry::instance();
auto moduleIds = registry.getAllModules();
for (ModuleId moduleId : moduleIds) {
IModuleConfig* moduleConfig = registry.getModuleConfig(moduleId);
if (!moduleConfig) continue;
std::string sectionName = moduleConfig->getConfigSectionName();
if (sectionName.empty()) continue;
json sectionJson;
if (moduleConfig->saveToJson(&sectionJson)) {
root[sectionName] = sectionJson;
}
}
std::ofstream file(filepath);
if (!file.is_open()) {
E2D_LOG_ERROR("无法创建配置文件: {}", filepath);
return ConfigSaveResult::error("无法创建配置文件: " + filepath);
}
file << root.dump(4);
file.close();
E2D_LOG_INFO("完整配置已成功保存到: {}", filepath);
return ConfigSaveResult::ok();
}
bool JsonConfigLoader::supportsFile(const std::string& filepath) const {
if (filepath.length() >= 5) {
std::string ext = filepath.substr(filepath.length() - 5);
for (char& c : ext) c = static_cast<char>(std::tolower(c));
return ext == ".json";
}
return false;
}
UniquePtr<ConfigLoader> JsonConfigLoader::clone() const {
return makeUnique<JsonConfigLoader>();
}
}

View File

@ -1,385 +0,0 @@
#include <extra2d/config/config_manager.h>
#include <extra2d/config/platform_config.h>
#include <extra2d/config/platform_detector.h>
#include <extra2d/utils/logger.h>
#include <sstream>
namespace extra2d {
ConfigManager::ConfigManager()
: m_nextCallbackId(1)
, m_autoSaveEnabled(false)
, m_autoSaveInterval(30.0f)
, m_autoSaveTimer(0.0f)
{
m_appConfig = AppConfig::createDefault();
}
ConfigManager::~ConfigManager() {
if (m_initialized) {
shutdown();
}
}
ConfigManager& ConfigManager::instance() {
static ConfigManager instance;
return instance;
}
bool ConfigManager::initialize(const std::string& configPath) {
if (m_initialized) {
E2D_LOG_WARN("ConfigManager already initialized");
return true;
}
std::lock_guard<std::mutex> lock(m_mutex);
m_configPath = configPath;
m_platformConfig = createPlatformConfig(PlatformType::Auto);
if (!m_platformConfig) {
E2D_LOG_ERROR("Failed to create platform config");
return false;
}
m_loader = makeUnique<JsonConfigLoader>();
if (!m_loader) {
E2D_LOG_ERROR("Failed to create config loader");
return false;
}
m_appConfig = AppConfig::createDefault();
m_appConfig.targetPlatform = PlatformDetector::detect();
m_initialized = true;
m_modified = false;
E2D_LOG_INFO("ConfigManager initialized for platform: {}",
m_platformConfig->platformName());
return true;
}
void ConfigManager::shutdown() {
if (!m_initialized) {
return;
}
std::lock_guard<std::mutex> lock(m_mutex);
if (m_autoSaveEnabled && m_modified) {
saveConfig();
}
m_changeCallbacks.clear();
m_rawValues.clear();
m_loader.reset();
m_platformConfig.reset();
m_initialized = false;
m_modified = false;
E2D_LOG_INFO("ConfigManager shutdown complete");
}
bool ConfigManager::isInitialized() const {
return m_initialized;
}
ConfigLoadResult ConfigManager::loadConfig(const std::string& filepath) {
std::lock_guard<std::mutex> lock(m_mutex);
std::string path = filepath.empty() ? m_configPath : filepath;
if (path.empty()) {
return ConfigLoadResult::error("No config file path specified");
}
if (!m_loader) {
return ConfigLoadResult::error("Config loader not initialized");
}
AppConfig loadedConfig;
ConfigLoadResult result = m_loader->load(path, loadedConfig);
if (result.success) {
m_appConfig.merge(loadedConfig);
if (!m_appConfig.validate()) {
E2D_LOG_WARN("Loaded config validation failed, using defaults");
m_appConfig = AppConfig::createDefault();
}
m_configPath = path;
m_modified = false;
E2D_LOG_INFO("Config loaded from: {}", path);
} else {
E2D_LOG_ERROR("Failed to load config from {}: {}", path, result.errorMessage);
}
return result;
}
ConfigSaveResult ConfigManager::saveConfig(const std::string& filepath) {
std::lock_guard<std::mutex> lock(m_mutex);
std::string path = filepath.empty() ? m_configPath : filepath;
if (path.empty()) {
return ConfigSaveResult::error("No config file path specified");
}
if (!m_loader) {
return ConfigSaveResult::error("Config loader not initialized");
}
ConfigSaveResult result = m_loader->save(path, m_appConfig);
if (result.success) {
m_configPath = path;
m_modified = false;
E2D_LOG_INFO("Config saved to: {}", path);
} else {
E2D_LOG_ERROR("Failed to save config to {}: {}", path, result.errorMessage);
}
return result;
}
ConfigLoadResult ConfigManager::loadConfigWithModules(const std::string& filepath) {
std::lock_guard<std::mutex> lock(m_mutex);
std::string path = filepath.empty() ? m_configPath : filepath;
if (path.empty()) {
return ConfigLoadResult::error("No config file path specified");
}
if (!m_loader) {
return ConfigLoadResult::error("Config loader not initialized");
}
ConfigLoadResult result = m_loader->loadWithModules(path);
if (result.success) {
m_configPath = path;
m_modified = false;
E2D_LOG_INFO("Full config (with modules) loaded from: {}", path);
} else {
E2D_LOG_ERROR("Failed to load full config from {}: {}", path, result.errorMessage);
}
return result;
}
ConfigSaveResult ConfigManager::saveConfigWithModules(const std::string& filepath) {
std::lock_guard<std::mutex> lock(m_mutex);
std::string path = filepath.empty() ? m_configPath : filepath;
if (path.empty()) {
return ConfigSaveResult::error("No config file path specified");
}
if (!m_loader) {
return ConfigSaveResult::error("Config loader not initialized");
}
ConfigSaveResult result = m_loader->saveWithModules(path);
if (result.success) {
m_configPath = path;
m_modified = false;
E2D_LOG_INFO("Full config (with modules) saved to: {}", path);
} else {
E2D_LOG_ERROR("Failed to save full config to {}: {}", path, result.errorMessage);
}
return result;
}
ConfigLoadResult ConfigManager::reload() {
return loadConfig(m_configPath);
}
const AppConfig& ConfigManager::appConfig() const {
return m_appConfig;
}
AppConfig& ConfigManager::appConfig() {
m_modified = true;
return m_appConfig;
}
void ConfigManager::setAppConfig(const AppConfig& config) {
std::lock_guard<std::mutex> lock(m_mutex);
m_appConfig = config;
m_modified = true;
E2D_LOG_INFO("App config updated");
}
PlatformConfig* ConfigManager::platformConfig() {
return m_platformConfig.get();
}
const PlatformConfig* ConfigManager::platformConfig() const {
return m_platformConfig.get();
}
int ConfigManager::registerChangeCallback(ConfigChangeCallback callback) {
std::lock_guard<std::mutex> lock(m_mutex);
int id = m_nextCallbackId++;
m_changeCallbacks[id] = std::move(callback);
E2D_LOG_DEBUG("Registered config change callback with id {}", id);
return id;
}
void ConfigManager::unregisterChangeCallback(int callbackId) {
std::lock_guard<std::mutex> lock(m_mutex);
auto it = m_changeCallbacks.find(callbackId);
if (it != m_changeCallbacks.end()) {
m_changeCallbacks.erase(it);
E2D_LOG_DEBUG("Unregistered config change callback {}", callbackId);
}
}
void ConfigManager::clearChangeCallbacks() {
std::lock_guard<std::mutex> lock(m_mutex);
m_changeCallbacks.clear();
E2D_LOG_DEBUG("Cleared all config change callbacks");
}
void ConfigManager::setValue(const std::string& section, const std::string& key, const std::string& value) {
std::lock_guard<std::mutex> lock(m_mutex);
std::string fullKey = section + "." + key;
ConfigChangeEvent event;
event.section = section;
event.field = key;
event.oldValue = m_rawValues[fullKey];
event.newValue = value;
m_rawValues[fullKey] = value;
m_modified = true;
notifyChangeCallbacks(event);
}
void ConfigManager::setValue(const std::string& section, const std::string& key, int value) {
std::ostringstream oss;
oss << value;
setValue(section, key, oss.str());
}
void ConfigManager::setValue(const std::string& section, const std::string& key, float value) {
std::ostringstream oss;
oss << value;
setValue(section, key, oss.str());
}
void ConfigManager::setValue(const std::string& section, const std::string& key, bool value) {
setValue(section, key, std::string(value ? "true" : "false"));
}
std::string ConfigManager::getValue(const std::string& section, const std::string& key,
const std::string& defaultValue) const {
std::lock_guard<std::mutex> lock(m_mutex);
std::string fullKey = section + "." + key;
auto it = m_rawValues.find(fullKey);
if (it != m_rawValues.end()) {
return it->second;
}
return defaultValue;
}
int ConfigManager::getIntValue(const std::string& section, const std::string& key, int defaultValue) const {
std::string value = getValue(section, key);
if (!value.empty()) {
try {
return std::stoi(value);
} catch (...) {
return defaultValue;
}
}
return defaultValue;
}
float ConfigManager::getFloatValue(const std::string& section, const std::string& key, float defaultValue) const {
std::string value = getValue(section, key);
if (!value.empty()) {
try {
return std::stof(value);
} catch (...) {
return defaultValue;
}
}
return defaultValue;
}
bool ConfigManager::getBoolValue(const std::string& section, const std::string& key, bool defaultValue) const {
std::string value = getValue(section, key);
if (!value.empty()) {
if (value == "true" || value == "1") return true;
if (value == "false" || value == "0") return false;
}
return defaultValue;
}
void ConfigManager::resetToDefaults() {
std::lock_guard<std::mutex> lock(m_mutex);
m_appConfig = AppConfig::createDefault();
m_rawValues.clear();
m_modified = true;
E2D_LOG_INFO("Config reset to defaults");
}
bool ConfigManager::hasUnsavedChanges() const {
return m_modified;
}
void ConfigManager::markModified() {
m_modified = true;
}
void ConfigManager::clearModified() {
m_modified = false;
}
void ConfigManager::setAutoSave(bool enabled, float interval) {
std::lock_guard<std::mutex> lock(m_mutex);
m_autoSaveEnabled = enabled;
m_autoSaveInterval = interval > 0.0f ? interval : 30.0f;
m_autoSaveTimer = 0.0f;
E2D_LOG_INFO("Auto save {} (interval: {}s)",
enabled ? "enabled" : "disabled", m_autoSaveInterval);
}
void ConfigManager::update(float deltaTime) {
if (!m_autoSaveEnabled || !m_modified) {
return;
}
m_autoSaveTimer += deltaTime;
if (m_autoSaveTimer >= m_autoSaveInterval) {
m_autoSaveTimer = 0.0f;
saveConfig();
}
}
void ConfigManager::notifyChangeCallbacks(const ConfigChangeEvent& event) {
for (const auto& pair : m_changeCallbacks) {
if (pair.second) {
pair.second(event);
}
}
}
}

View File

@ -1,114 +0,0 @@
#include <extra2d/config/config_module.h>
#include <extra2d/config/module_registry.h>
#include <extra2d/utils/logger.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace extra2d {
static ModuleId s_configModuleId = INVALID_MODULE_ID;
ModuleId get_config_module_id() {
return s_configModuleId;
}
bool ConfigModuleConfig::loadFromJson(const void* jsonData) {
if (!jsonData) return false;
try {
const json& j = *static_cast<const json*>(jsonData);
if (j.contains("configPath")) {
configPath = j["configPath"].get<std::string>();
}
return true;
} catch (...) {
return false;
}
}
bool ConfigModuleConfig::saveToJson(void* jsonData) const {
if (!jsonData) return false;
try {
json& j = *static_cast<json*>(jsonData);
j["configPath"] = configPath;
return true;
} catch (...) {
return false;
}
}
ConfigModuleInitializer::ConfigModuleInitializer()
: moduleId_(INVALID_MODULE_ID)
, initialized_(false) {
}
ConfigModuleInitializer::~ConfigModuleInitializer() {
if (initialized_) {
shutdown();
}
}
bool ConfigModuleInitializer::initialize(const IModuleConfig* config) {
if (initialized_) return true;
const ConfigModuleConfig* configModule = dynamic_cast<const ConfigModuleConfig*>(config);
if (!configPath_.empty()) {
if (!ConfigManager::instance().initialize(configPath_)) {
if (!ConfigManager::instance().initialize()) {
return false;
}
}
} else {
if (!ConfigManager::instance().initialize()) {
return false;
}
}
if (configModule && !configModule->appConfig.appName.empty()) {
ConfigManager::instance().setAppConfig(configModule->appConfig);
} else if (!appConfig_.appName.empty()) {
ConfigManager::instance().setAppConfig(appConfig_);
}
initialized_ = true;
E2D_LOG_INFO("Config module initialized");
return true;
}
void ConfigModuleInitializer::shutdown() {
if (!initialized_) return;
E2D_LOG_INFO("Config module shutting down");
ConfigManager::instance().shutdown();
initialized_ = false;
}
void register_config_module() {
if (s_configModuleId != INVALID_MODULE_ID) return;
s_configModuleId = ModuleRegistry::instance().registerModule(
makeUnique<ConfigModuleConfig>(),
[]() -> UniquePtr<IModuleInitializer> {
auto initializer = makeUnique<ConfigModuleInitializer>();
initializer->setModuleId(s_configModuleId);
return initializer;
}
);
}
namespace {
struct ConfigModuleAutoRegister {
ConfigModuleAutoRegister() {
register_config_module();
}
};
static ConfigModuleAutoRegister s_autoRegister;
}
} // namespace extra2d

View File

@ -1,211 +0,0 @@
#include <extra2d/config/module_registry.h>
#include <algorithm>
#include <cstdio>
namespace extra2d {
/**
* @brief
* 使线
* @return
*/
ModuleRegistry& ModuleRegistry::instance() {
static ModuleRegistry instance;
return instance;
}
/**
* @brief
*
* @param config
* @param initializerFactory
* @return
*/
ModuleId ModuleRegistry::registerModule(
UniquePtr<IModuleConfig> config,
ModuleInitializerFactory initializerFactory
) {
if (!config) {
std::fprintf(stderr, "[ERROR] Cannot register null module config\n");
return INVALID_MODULE_ID;
}
std::lock_guard<std::mutex> lock(mutex_);
ModuleInfo info = config->getModuleInfo();
if (nameToId_.find(info.name) != nameToId_.end()) {
std::fprintf(stderr, "[ERROR] Module '%s' already registered\n", info.name.c_str());
return INVALID_MODULE_ID;
}
ModuleId id = generateId();
ModuleEntry entry;
entry.id = id;
entry.config = std::move(config);
entry.initializerFactory = std::move(initializerFactory);
entry.initialized = false;
modules_[id] = std::move(entry);
nameToId_[info.name] = id;
return id;
}
/**
* @brief
*
* @param id
* @return true
*/
bool ModuleRegistry::unregisterModule(ModuleId id) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = modules_.find(id);
if (it == modules_.end()) {
return false;
}
ModuleInfo info = it->second.config->getModuleInfo();
nameToId_.erase(info.name);
modules_.erase(it);
return true;
}
/**
* @brief
* @param id
* @return nullptr
*/
IModuleConfig* ModuleRegistry::getModuleConfig(ModuleId id) const {
std::lock_guard<std::mutex> lock(mutex_);
auto it = modules_.find(id);
if (it != modules_.end()) {
return it->second.config.get();
}
return nullptr;
}
/**
* @brief
* @param name
* @return nullptr
*/
IModuleConfig* ModuleRegistry::getModuleConfigByName(const std::string& name) const {
std::lock_guard<std::mutex> lock(mutex_);
auto nameIt = nameToId_.find(name);
if (nameIt == nameToId_.end()) {
return nullptr;
}
auto moduleIt = modules_.find(nameIt->second);
if (moduleIt != modules_.end()) {
return moduleIt->second.config.get();
}
return nullptr;
}
/**
* @brief
* @param id
* @return nullptr
*/
IModuleInitializer* ModuleRegistry::getInitializer(ModuleId id) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = modules_.find(id);
if (it == modules_.end() || !it->second.initializerFactory) {
return nullptr;
}
if (!it->second.initializer) {
it->second.initializer = it->second.initializerFactory();
}
return it->second.initializer.get();
}
/**
* @brief
* @return
*/
std::vector<ModuleId> ModuleRegistry::getAllModules() const {
std::lock_guard<std::mutex> lock(mutex_);
std::vector<ModuleId> result;
result.reserve(modules_.size());
for (const auto& pair : modules_) {
result.push_back(pair.first);
}
return result;
}
/**
* @brief
*
* @return
*/
std::vector<ModuleId> ModuleRegistry::getInitializationOrder() const {
std::lock_guard<std::mutex> lock(mutex_);
std::vector<std::pair<ModuleId, int>> modulePriorities;
modulePriorities.reserve(modules_.size());
for (const auto& pair : modules_) {
ModuleInfo info = pair.second.config->getModuleInfo();
if (info.enabled) {
modulePriorities.emplace_back(pair.first, static_cast<int>(info.priority));
}
}
std::sort(modulePriorities.begin(), modulePriorities.end(),
[](const auto& a, const auto& b) {
return a.second < b.second;
});
std::vector<ModuleId> result;
result.reserve(modulePriorities.size());
for (const auto& pair : modulePriorities) {
result.push_back(pair.first);
}
return result;
}
/**
* @brief
* @param id
* @return true
*/
bool ModuleRegistry::hasModule(ModuleId id) const {
std::lock_guard<std::mutex> lock(mutex_);
return modules_.find(id) != modules_.end();
}
/**
* @brief
*/
void ModuleRegistry::clear() {
std::lock_guard<std::mutex> lock(mutex_);
modules_.clear();
nameToId_.clear();
nextId_ = 1;
}
/**
* @brief
* @return
*/
ModuleId ModuleRegistry::generateId() {
return nextId_++;
}
}

View File

@ -0,0 +1,96 @@
#include <extra2d/core/registry.h>
#include <extra2d/utils/logger.h>
#include <algorithm>
#include <queue>
#include <unordered_set>
#include <iostream>
namespace extra2d {
Registry& Registry::instance() {
static Registry instance;
return instance;
}
bool Registry::init() {
auto sorted = topologicalSort();
std::cout << "[Registry] Initializing " << sorted.size() << " modules..." << std::endl;
for (auto* module : sorted) {
std::cout << "[Registry] Initializing module: " << module->name() << std::endl;
if (!module->init()) {
std::cerr << "[Registry] Failed to initialize module: " << module->name() << std::endl;
return false;
}
std::cout << "[Registry] Module " << module->name() << " initialized successfully" << std::endl;
}
std::cout << "[Registry] All modules initialized" << std::endl;
return true;
}
void Registry::shutdown() {
auto sorted = topologicalSort();
// 反向关闭
for (auto it = sorted.rbegin(); it != sorted.rend(); ++it) {
(*it)->shutdown();
}
}
void Registry::clear() {
shutdown();
modules_.clear();
}
std::vector<Module*> Registry::topologicalSort() {
std::vector<Module*> result;
std::unordered_map<Module*, int> inDegree;
std::unordered_map<Module*, std::vector<Module*>> adj;
// 构建图
for (auto& [typeIdx, module] : modules_) {
inDegree[module.get()] = 0;
}
for (auto& [typeIdx, module] : modules_) {
for (auto& depType : module->deps()) {
Module* dep = get(depType);
if (dep) {
adj[dep].push_back(module.get());
inDegree[module.get()]++;
}
}
}
// 优先级队列(优先级小的先处理)
auto cmp = [](Module* a, Module* b) {
return a->priority() > b->priority();
};
std::priority_queue<Module*, std::vector<Module*>, decltype(cmp)> pq(cmp);
for (auto& [mod, degree] : inDegree) {
if (degree == 0) {
pq.push(mod);
}
}
// 拓扑排序
while (!pq.empty()) {
Module* curr = pq.top();
pq.pop();
result.push_back(curr);
for (Module* next : adj[curr]) {
inDegree[next]--;
if (inDegree[next] == 0) {
pq.push(next);
}
}
}
return result;
}
} // namespace extra2d

View File

@ -1,23 +0,0 @@
#include <extra2d/debug/debug_config.h>
#include <algorithm>
namespace extra2d {
bool DebugConfigData::hasDebugFlag(const std::string& flag) const {
return std::find(debugFlags.begin(), debugFlags.end(), flag) != debugFlags.end();
}
void DebugConfigData::addDebugFlag(const std::string& flag) {
if (!hasDebugFlag(flag)) {
debugFlags.push_back(flag);
}
}
void DebugConfigData::removeDebugFlag(const std::string& flag) {
auto it = std::find(debugFlags.begin(), debugFlags.end(), flag);
if (it != debugFlags.end()) {
debugFlags.erase(it);
}
}
}

View File

@ -1,6 +1,6 @@
#include <algorithm> #include <algorithm>
#include <extra2d/graphics/camera.h> #include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/viewport_adapter.h> #include <extra2d/graphics/camera/viewport_adapter.h>
#include <glm/gtc/matrix_inverse.hpp> #include <glm/gtc/matrix_inverse.hpp>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>

View File

@ -1,4 +1,4 @@
#include <extra2d/graphics/viewport_adapter.h> #include <extra2d/graphics/camera/viewport_adapter.h>
#include <algorithm> #include <algorithm>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>

View File

@ -1,5 +1,5 @@
#include <extra2d/graphics/opengl/gl_renderer.h> #include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
namespace extra2d { namespace extra2d {

View File

@ -1,4 +1,4 @@
#include <extra2d/graphics/render_command.h> #include <extra2d/graphics/core/render_command.h>
#include <algorithm> #include <algorithm>
namespace extra2d { namespace extra2d {

View File

@ -0,0 +1,78 @@
#include <extra2d/graphics/core/render_module.h>
#include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/graphics/opengl/gl_shader.h>
#include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/core/registry.h>
#include <extra2d/core/service_locator.h>
#include <extra2d/app/application.h>
#include <extra2d/platform/window_module.h>
#include <extra2d/utils/logger.h>
#include <SDL.h>
namespace extra2d {
RenderModule::RenderModule(const Cfg& cfg) : cfg_(cfg) {}
RenderModule::~RenderModule() {
if (initialized_) {
shutdown();
}
}
static std::string getExecutableDir() {
char* basePath = SDL_GetBasePath();
if (basePath) {
std::string path(basePath);
SDL_free(basePath);
return path;
}
return "./";
}
bool RenderModule::init() {
if (initialized_) return true;
// 获取WindowModule依赖
auto* winMod = Registry::instance().get<WindowModule>();
if (!winMod || !winMod->win()) {
return false;
}
// 初始化ShaderManager
if (!ShaderManager::getInstance().isInitialized()) {
auto factory = makeShared<GLShaderFactory>();
std::string shaderDir = getExecutableDir() + "shaders/";
std::string cacheDir = getExecutableDir() + "shader_cache/";
if (!ShaderManager::getInstance().init(shaderDir, cacheDir, factory)) {
E2D_LOG_WARN("Failed to initialize ShaderManager with dir: {}", shaderDir);
}
}
// 创建渲染后端
renderer_ = RenderBackend::create(cfg_.backend);
if (!renderer_) {
return false;
}
// 初始化渲染器
if (!renderer_->init(winMod->win())) {
renderer_.reset();
return false;
}
initialized_ = true;
return true;
}
void RenderModule::shutdown() {
if (!initialized_) return;
if (renderer_) {
renderer_->shutdown();
renderer_.reset();
}
initialized_ = false;
}
} // namespace extra2d

View File

@ -1,6 +1,6 @@
#include <glad/glad.h> #include <glad/glad.h>
#include <extra2d/graphics/opengl/gl_texture.h> #include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/render_target.h> #include <extra2d/graphics/core/render_target.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION

View File

@ -1,4 +1,4 @@
#include <extra2d/graphics/gpu_context.h> #include <extra2d/graphics/memory/gpu_context.h>
namespace extra2d { namespace extra2d {

View File

@ -1,5 +1,5 @@
#include <algorithm> #include <algorithm>
#include <extra2d/graphics/vram_manager.h> #include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
namespace extra2d { namespace extra2d {

View File

@ -2,12 +2,12 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include <extra2d/graphics/gpu_context.h> #include <extra2d/graphics/memory/gpu_context.h>
#include <extra2d/graphics/opengl/gl_font_atlas.h> #include <extra2d/graphics/opengl/gl_font_atlas.h>
#include <extra2d/graphics/opengl/gl_renderer.h> #include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/graphics/opengl/gl_texture.h> #include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/shader_manager.h> #include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/graphics/vram_manager.h> #include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/platform/iwindow.h> #include <extra2d/platform/iwindow.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
#include <vector> #include <vector>
@ -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()) {

View File

@ -1,6 +1,6 @@
#include <extra2d/graphics/opengl/gl_texture.h> #include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/gpu_context.h> #include <extra2d/graphics/memory/gpu_context.h>
#include <extra2d/graphics/vram_manager.h> #include <extra2d/graphics/memory/vram_manager.h>
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include <cstring> #include <cstring>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>

View File

@ -1,214 +0,0 @@
#include <extra2d/graphics/render_module.h>
#include <extra2d/config/module_registry.h>
#include <extra2d/graphics/opengl/gl_shader.h>
#include <extra2d/graphics/shader_manager.h>
#include <extra2d/platform/iwindow.h>
#include <extra2d/utils/logger.h>
#include <nlohmann/json.hpp>
#include <algorithm>
using json = nlohmann::json;
namespace extra2d {
static ModuleId s_renderModuleId = INVALID_MODULE_ID;
ModuleId get_render_module_id() {
return s_renderModuleId;
}
bool RenderModuleConfig::validate() const {
if (targetFPS < 1 || targetFPS > 240) {
return false;
}
if (multisamples != 0 && multisamples != 2 && multisamples != 4 &&
multisamples != 8 && multisamples != 16) {
return false;
}
if (spriteBatchSize <= 0) {
return false;
}
return true;
}
void RenderModuleConfig::applyPlatformConstraints(PlatformType platform) {
switch (platform) {
case PlatformType::Switch:
if (multisamples > 4) {
multisamples = 4;
}
if (sRGBFramebuffer) {
sRGBFramebuffer = false;
}
if (targetFPS > 60) {
targetFPS = 60;
}
break;
default:
break;
}
}
void RenderModuleConfig::resetToDefaults() {
backend = BackendType::OpenGL;
vsync = true;
targetFPS = 60;
multisamples = 0;
sRGBFramebuffer = false;
spriteBatchSize = 1000;
}
bool RenderModuleConfig::loadFromJson(const void* jsonData) {
if (!jsonData) return false;
try {
const json& j = *static_cast<const json*>(jsonData);
if (j.contains("backend")) {
std::string backendStr = j["backend"].get<std::string>();
if (backendStr == "opengl") {
backend = BackendType::OpenGL;
}
}
if (j.contains("vsync")) {
vsync = j["vsync"].get<bool>();
}
if (j.contains("targetFPS")) {
targetFPS = j["targetFPS"].get<int>();
}
if (j.contains("multisamples")) {
multisamples = j["multisamples"].get<int>();
}
if (j.contains("sRGBFramebuffer")) {
sRGBFramebuffer = j["sRGBFramebuffer"].get<bool>();
}
if (j.contains("spriteBatchSize")) {
spriteBatchSize = j["spriteBatchSize"].get<int>();
}
return true;
} catch (...) {
return false;
}
}
bool RenderModuleConfig::saveToJson(void* jsonData) const {
if (!jsonData) return false;
try {
json& j = *static_cast<json*>(jsonData);
j["backend"] = "opengl";
j["vsync"] = vsync;
j["targetFPS"] = targetFPS;
j["multisamples"] = multisamples;
j["sRGBFramebuffer"] = sRGBFramebuffer;
j["spriteBatchSize"] = spriteBatchSize;
return true;
} catch (...) {
return false;
}
}
RenderModuleInitializer::RenderModuleInitializer()
: moduleId_(INVALID_MODULE_ID)
, window_(nullptr)
, initialized_(false) {
}
RenderModuleInitializer::~RenderModuleInitializer() {
if (initialized_) {
shutdown();
}
}
std::vector<ModuleId> RenderModuleInitializer::getDependencies() const {
return {};
}
bool RenderModuleInitializer::initialize(const IModuleConfig* config) {
if (initialized_) return true;
if (!config) return false;
const RenderModuleConfig* renderConfig = dynamic_cast<const RenderModuleConfig*>(config);
if (!renderConfig) return false;
if (!renderConfig->validate()) return false;
if (!window_) {
E2D_LOG_ERROR("Render module requires window to be set");
return false;
}
auto shaderFactory = std::make_shared<GLShaderFactory>();
if (!ShaderManager::getInstance().init(shaderFactory, "extra2d")) {
E2D_LOG_WARN("Failed to initialize ShaderManager with default paths");
}
if (!ShaderManager::getInstance().loadBuiltinShaders()) {
E2D_LOG_WARN("Failed to load some builtin shaders");
}
renderer_ = RenderBackend::create(renderConfig->backend);
if (!renderer_) {
E2D_LOG_ERROR("Failed to create render backend");
return false;
}
if (!renderer_->init(window_)) {
E2D_LOG_ERROR("Failed to initialize renderer");
renderer_.reset();
return false;
}
initialized_ = true;
E2D_LOG_INFO("Render module initialized");
return true;
}
void RenderModuleInitializer::shutdown() {
if (!initialized_) return;
if (renderer_) {
renderer_->shutdown();
renderer_.reset();
}
ShaderManager::getInstance().shutdown();
initialized_ = false;
E2D_LOG_INFO("Render module shutdown");
}
void register_render_module() {
if (s_renderModuleId != INVALID_MODULE_ID) return;
s_renderModuleId = ModuleRegistry::instance().registerModule(
makeUnique<RenderModuleConfig>(),
[]() -> UniquePtr<IModuleInitializer> {
auto initializer = makeUnique<RenderModuleInitializer>();
initializer->setModuleId(s_renderModuleId);
return initializer;
}
);
}
namespace {
struct RenderModuleAutoRegister {
RenderModuleAutoRegister() {
register_render_module();
}
};
static RenderModuleAutoRegister s_autoRegister;
}
} // namespace extra2d

View File

@ -1,4 +1,4 @@
#include <extra2d/graphics/shader_cache.h> #include <extra2d/graphics/shader/shader_cache.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
#include <chrono> #include <chrono>
#include <filesystem> #include <filesystem>

View File

@ -1,4 +1,4 @@
#include <extra2d/graphics/shader_hot_reloader.h> #include <extra2d/graphics/shader/shader_hot_reloader.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
#include <chrono> #include <chrono>
#include <filesystem> #include <filesystem>

View File

@ -1,4 +1,4 @@
#include <extra2d/graphics/shader_loader.h> #include <extra2d/graphics/shader/shader_loader.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>

View File

@ -1,4 +1,4 @@
#include <extra2d/graphics/shader_manager.h> #include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
namespace extra2d { namespace extra2d {

View File

@ -1,5 +1,5 @@
#include <extra2d/graphics/shader_manager.h> #include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/graphics/shader_preset.h> #include <extra2d/graphics/shader/shader_preset.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
namespace extra2d { namespace extra2d {

View File

@ -1,4 +1,4 @@
#include <extra2d/graphics/alpha_mask.h> #include <extra2d/graphics/texture/alpha_mask.h>
namespace extra2d { namespace extra2d {

View File

@ -1,4 +1,4 @@
#include <extra2d/graphics/texture_atlas.h> #include <extra2d/graphics/texture/texture_atlas.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>

View File

@ -1,5 +1,5 @@
#include <extra2d/graphics/texture_pool.h> #include <extra2d/graphics/texture/texture_pool.h>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/scene/scene.h> #include <extra2d/scene/scene.h>
#include <algorithm> #include <algorithm>

View File

@ -1,4 +1,4 @@
#include <extra2d/platform/platform_module.h> #include <extra2d/platform/backend_factory.h>
namespace extra2d { namespace extra2d {

View File

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

View File

@ -1,213 +1,49 @@
#include <extra2d/platform/input_module.h> #include <extra2d/platform/input_module.h>
#include <extra2d/config/module_registry.h>
#include <extra2d/core/service_locator.h> #include <extra2d/core/service_locator.h>
#include <extra2d/platform/window_module.h> #include <extra2d/core/registry.h>
#include <extra2d/services/event_service.h> #include <extra2d/services/event_service.h>
#include <extra2d/utils/logger.h> #include <extra2d/platform/window_module.h>
#include <nlohmann/json.hpp>
#include "backends/sdl2/sdl2_input.h"
using json = nlohmann::json;
namespace extra2d { namespace extra2d {
static ModuleId s_inputModuleId = INVALID_MODULE_ID; InputModule::InputModule(const Cfg& cfg) : cfg_(cfg) {}
ModuleId get_input_module_id() { InputModule::~InputModule() {
return s_inputModuleId;
}
bool InputModuleConfig::validate() const {
return inputConfig.isDeadzoneValid();
}
void InputModuleConfig::applyPlatformConstraints(PlatformType platform) {
#ifdef __SWITCH__
(void)platform;
inputConfig.enableVibration = true;
inputConfig.maxGamepads = 2;
#else
(void)platform;
#endif
}
bool InputModuleConfig::loadFromJson(const void* jsonData) {
if (!jsonData) return false;
try {
const json& j = *static_cast<const json*>(jsonData);
if (j.contains("enabled")) {
inputConfig.enabled = j["enabled"].get<bool>();
}
if (j.contains("rawMouseInput")) {
inputConfig.rawMouseInput = j["rawMouseInput"].get<bool>();
}
if (j.contains("mouseSensitivity")) {
inputConfig.mouseSensitivity = j["mouseSensitivity"].get<float>();
}
if (j.contains("invertMouseY")) {
inputConfig.invertMouseY = j["invertMouseY"].get<bool>();
}
if (j.contains("invertMouseX")) {
inputConfig.invertMouseX = j["invertMouseX"].get<bool>();
}
if (j.contains("deadzone")) {
inputConfig.deadzone = j["deadzone"].get<float>();
}
if (j.contains("triggerThreshold")) {
inputConfig.triggerThreshold = j["triggerThreshold"].get<float>();
}
if (j.contains("enableVibration")) {
inputConfig.enableVibration = j["enableVibration"].get<bool>();
}
if (j.contains("maxGamepads")) {
inputConfig.maxGamepads = j["maxGamepads"].get<int>();
}
if (j.contains("autoConnectGamepads")) {
inputConfig.autoConnectGamepads = j["autoConnectGamepads"].get<bool>();
}
if (j.contains("gamepadMappingFile")) {
inputConfig.gamepadMappingFile = j["gamepadMappingFile"].get<std::string>();
}
return true;
} catch (...) {
return false;
}
}
bool InputModuleConfig::saveToJson(void* jsonData) const {
if (!jsonData) return false;
try {
json& j = *static_cast<json*>(jsonData);
j["enabled"] = inputConfig.enabled;
j["rawMouseInput"] = inputConfig.rawMouseInput;
j["mouseSensitivity"] = inputConfig.mouseSensitivity;
j["invertMouseY"] = inputConfig.invertMouseY;
j["invertMouseX"] = inputConfig.invertMouseX;
j["deadzone"] = inputConfig.deadzone;
j["triggerThreshold"] = inputConfig.triggerThreshold;
j["enableVibration"] = inputConfig.enableVibration;
j["maxGamepads"] = inputConfig.maxGamepads;
j["autoConnectGamepads"] = inputConfig.autoConnectGamepads;
j["gamepadMappingFile"] = inputConfig.gamepadMappingFile;
return true;
} catch (...) {
return false;
}
}
InputModuleInitializer::InputModuleInitializer()
: moduleId_(INVALID_MODULE_ID)
, input_(nullptr)
, initialized_(false) {
}
InputModuleInitializer::~InputModuleInitializer() {
if (initialized_) { if (initialized_) {
shutdown(); shutdown();
} }
} }
std::vector<ModuleId> InputModuleInitializer::getDependencies() const { bool InputModule::init() {
return { get_window_module_id() };
}
bool InputModuleInitializer::initialize(const IModuleConfig* config) {
if (initialized_) return true; if (initialized_) return true;
const InputModuleConfig* inputConfig = dynamic_cast<const InputModuleConfig*>(config); // 获取WindowModule依赖
if (!inputConfig) { auto* winMod = Registry::instance().get<WindowModule>();
E2D_LOG_ERROR("Invalid input module config"); if (!winMod || !winMod->win()) {
return false; return false;
} }
config_ = inputConfig->inputConfig; // 获取输入接口
input_ = winMod->win()->input();
auto& registry = ModuleRegistry::instance();
auto* windowInitializer = registry.getInitializer(get_window_module_id());
if (!windowInitializer) {
E2D_LOG_ERROR("Window module not found - Input module depends on it");
return false;
}
auto* windowModule = static_cast<WindowModuleInitializer*>(windowInitializer);
IWindow* window = windowModule->getWindow();
if (!window) {
E2D_LOG_ERROR("Window not created - cannot get input interface");
return false;
}
input_ = window->input();
if (!input_) { if (!input_) {
E2D_LOG_ERROR("Input interface not available from window");
return false; return false;
} }
SDL2Input* sdl2Input = dynamic_cast<SDL2Input*>(input_);
if (sdl2Input) {
auto eventService = ServiceLocator::instance().getService<IEventService>();
if (eventService) {
sdl2Input->setEventCallback([eventService](const Event& event) {
Event mutableEvent = event;
eventService->dispatch(mutableEvent);
});
E2D_LOG_INFO("Input events connected to EventService");
} else {
E2D_LOG_WARN("EventService not available - input events will not be dispatched");
}
}
initialized_ = true; initialized_ = true;
E2D_LOG_INFO("Input module initialized");
E2D_LOG_INFO(" Deadzone: {}", config_.deadzone);
E2D_LOG_INFO(" Mouse sensitivity: {}", config_.mouseSensitivity);
E2D_LOG_INFO(" Vibration: {}", config_.enableVibration ? "enabled" : "disabled");
return true; return true;
} }
void InputModuleInitializer::shutdown() { void InputModule::shutdown() {
if (!initialized_) return; if (!initialized_) return;
E2D_LOG_INFO("Input module shutting down");
input_ = nullptr; input_ = nullptr;
initialized_ = false; initialized_ = false;
} }
void InputModuleInitializer::update() { void InputModule::update() {
if (!initialized_ || !input_) return; if (initialized_ && input_) {
input_->update();
input_->update(); }
} }
void register_input_module() { } // namespace extra2d
if (s_inputModuleId != INVALID_MODULE_ID) return;
s_inputModuleId = ModuleRegistry::instance().registerModule(
makeUnique<InputModuleConfig>(),
[]() -> UniquePtr<IModuleInitializer> {
auto initializer = makeUnique<InputModuleInitializer>();
initializer->setModuleId(s_inputModuleId);
return initializer;
}
);
}
namespace {
struct InputModuleAutoRegister {
InputModuleAutoRegister() {
register_input_module();
}
};
static InputModuleAutoRegister s_autoRegister;
}
}

View File

@ -1,168 +0,0 @@
#include <extra2d/platform/platform_init_module.h>
#include <extra2d/config/module_registry.h>
#include <extra2d/config/config_manager.h>
#include <extra2d/utils/logger.h>
#include <nlohmann/json.hpp>
#ifdef __SWITCH__
#include <switch.h>
#endif
using json = nlohmann::json;
namespace extra2d {
static ModuleId s_platformModuleId = INVALID_MODULE_ID;
ModuleId get_platform_module_id() {
return s_platformModuleId;
}
bool PlatformModuleConfig::loadFromJson(const void* jsonData) {
if (!jsonData) return false;
try {
const json& j = *static_cast<const json*>(jsonData);
if (j.contains("targetPlatform")) {
int platform = j["targetPlatform"].get<int>();
if (platform >= 0 && platform <= 5) {
targetPlatform = static_cast<PlatformType>(platform);
}
}
return true;
} catch (...) {
return false;
}
}
bool PlatformModuleConfig::saveToJson(void* jsonData) const {
if (!jsonData) return false;
try {
json& j = *static_cast<json*>(jsonData);
j["targetPlatform"] = static_cast<int>(targetPlatform);
return true;
} catch (...) {
return false;
}
}
PlatformModuleInitializer::PlatformModuleInitializer()
: moduleId_(INVALID_MODULE_ID)
, initialized_(false)
, targetPlatform_(PlatformType::Auto)
, resolvedPlatform_(PlatformType::Windows) {
}
PlatformModuleInitializer::~PlatformModuleInitializer() {
if (initialized_) {
shutdown();
}
}
bool PlatformModuleInitializer::initialize(const IModuleConfig* config) {
if (initialized_) return true;
const PlatformModuleConfig* platformConfig = dynamic_cast<const PlatformModuleConfig*>(config);
if (platformConfig) {
targetPlatform_ = platformConfig->targetPlatform;
}
resolvedPlatform_ = targetPlatform_;
if (resolvedPlatform_ == PlatformType::Auto) {
#ifdef __SWITCH__
resolvedPlatform_ = PlatformType::Switch;
#else
#ifdef _WIN32
resolvedPlatform_ = PlatformType::Windows;
#elif defined(__linux__)
resolvedPlatform_ = PlatformType::Linux;
#elif defined(__APPLE__)
resolvedPlatform_ = PlatformType::macOS;
#else
resolvedPlatform_ = PlatformType::Windows;
#endif
#endif
}
platformConfig_ = createPlatformConfig(resolvedPlatform_);
if (!platformConfig_) {
E2D_LOG_ERROR("Failed to create platform config");
return false;
}
if (resolvedPlatform_ == PlatformType::Switch) {
if (!initSwitch()) {
return false;
}
}
initialized_ = true;
E2D_LOG_INFO("Platform module initialized ({})", getPlatformTypeName(resolvedPlatform_));
return true;
}
bool PlatformModuleInitializer::initSwitch() {
#ifdef __SWITCH__
Result rc;
rc = romfsInit();
if (R_SUCCEEDED(rc)) {
E2D_LOG_INFO("RomFS initialized successfully");
} else {
E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem", rc);
}
rc = socketInitializeDefault();
if (R_FAILED(rc)) {
E2D_LOG_WARN("socketInitializeDefault failed, nxlink will not be available");
}
#endif
return true;
}
void PlatformModuleInitializer::shutdown() {
if (!initialized_) return;
E2D_LOG_INFO("Platform module shutting down");
if (resolvedPlatform_ == PlatformType::Switch) {
shutdownSwitch();
}
platformConfig_.reset();
initialized_ = false;
}
void PlatformModuleInitializer::shutdownSwitch() {
#ifdef __SWITCH__
romfsExit();
socketExit();
#endif
}
void register_platform_module() {
if (s_platformModuleId != INVALID_MODULE_ID) return;
s_platformModuleId = ModuleRegistry::instance().registerModule(
makeUnique<PlatformModuleConfig>(),
[]() -> UniquePtr<IModuleInitializer> {
auto initializer = makeUnique<PlatformModuleInitializer>();
initializer->setModuleId(s_platformModuleId);
return initializer;
}
);
}
namespace {
struct PlatformModuleAutoRegister {
PlatformModuleAutoRegister() {
register_platform_module();
}
};
static PlatformModuleAutoRegister s_autoRegister;
}
} // namespace extra2d

View File

@ -1,258 +1,92 @@
#include <extra2d/platform/window_module.h> #include <extra2d/platform/window_module.h>
#include <extra2d/config/module_registry.h> #include <extra2d/platform/backend_factory.h>
#include <extra2d/platform/platform_module.h> #include <extra2d/core/service_locator.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
#include <nlohmann/json.hpp>
#include <SDL.h> #include <SDL.h>
#ifdef __SWITCH__ #ifdef __SWITCH__
#include <switch.h> #include <switch.h>
#endif #endif
using json = nlohmann::json;
namespace extra2d { namespace extra2d {
static ModuleId s_windowModuleId = INVALID_MODULE_ID; // 前向声明 SDL2 后端初始化函数
void initSDL2Backend();
ModuleId get_window_module_id() { WindowModule::WindowModule(const Cfg& cfg) : cfg_(cfg) {}
return s_windowModuleId;
}
void WindowModuleConfig::applyPlatformConstraints(PlatformType platform) { WindowModule::~WindowModule() {
#ifdef __SWITCH__
(void)platform;
windowConfig.mode = WindowMode::Fullscreen;
windowConfig.resizable = false;
windowConfig.highDPI = false;
windowConfig.width = 1920;
windowConfig.height = 1080;
#else
(void)platform;
#endif
}
bool WindowModuleConfig::loadFromJson(const void* jsonData) {
if (!jsonData) return false;
try {
const json& j = *static_cast<const json*>(jsonData);
if (j.contains("title")) {
windowConfig.title = j["title"].get<std::string>();
}
if (j.contains("width")) {
windowConfig.width = j["width"].get<int>();
}
if (j.contains("height")) {
windowConfig.height = j["height"].get<int>();
}
if (j.contains("minWidth")) {
windowConfig.minWidth = j["minWidth"].get<int>();
}
if (j.contains("minHeight")) {
windowConfig.minHeight = j["minHeight"].get<int>();
}
if (j.contains("fullscreen")) {
windowConfig.mode = j["fullscreen"].get<bool>() ? WindowMode::Fullscreen : WindowMode::Windowed;
}
if (j.contains("mode")) {
std::string modeStr = j["mode"].get<std::string>();
if (modeStr == "fullscreen") {
windowConfig.mode = WindowMode::Fullscreen;
} else if (modeStr == "borderless") {
windowConfig.mode = WindowMode::Borderless;
} else {
windowConfig.mode = WindowMode::Windowed;
}
}
if (j.contains("vsync")) {
windowConfig.vsync = j["vsync"].get<bool>();
}
if (j.contains("resizable")) {
windowConfig.resizable = j["resizable"].get<bool>();
}
if (j.contains("highDPI")) {
windowConfig.highDPI = j["highDPI"].get<bool>();
}
if (j.contains("multisamples")) {
windowConfig.multisamples = j["multisamples"].get<int>();
}
return true;
} catch (...) {
return false;
}
}
bool WindowModuleConfig::saveToJson(void* jsonData) const {
if (!jsonData) return false;
try {
json& j = *static_cast<json*>(jsonData);
j["title"] = windowConfig.title;
j["width"] = windowConfig.width;
j["height"] = windowConfig.height;
j["minWidth"] = windowConfig.minWidth;
j["minHeight"] = windowConfig.minHeight;
switch (windowConfig.mode) {
case WindowMode::Fullscreen:
j["mode"] = "fullscreen";
break;
case WindowMode::Borderless:
j["mode"] = "borderless";
break;
default:
j["mode"] = "windowed";
break;
}
j["vsync"] = windowConfig.vsync;
j["resizable"] = windowConfig.resizable;
j["highDPI"] = windowConfig.highDPI;
j["multisamples"] = windowConfig.multisamples;
return true;
} catch (...) {
return false;
}
}
WindowModuleInitializer::WindowModuleInitializer()
: moduleId_(INVALID_MODULE_ID)
, initialized_(false)
, sdl2Initialized_(false) {
}
WindowModuleInitializer::~WindowModuleInitializer() {
if (initialized_) { if (initialized_) {
shutdown(); shutdown();
} }
} }
bool WindowModuleInitializer::initSDL2() { bool WindowModule::init() {
Uint32 initFlags = SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER;
#ifdef __SWITCH__
initFlags |= SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER;
#endif
if (SDL_Init(initFlags) != 0) {
E2D_LOG_ERROR("Failed to initialize SDL2: {}", SDL_GetError());
return false;
}
sdl2Initialized_ = true;
E2D_LOG_INFO("SDL2 initialized successfully");
return true;
}
void WindowModuleInitializer::shutdownSDL2() {
if (!sdl2Initialized_) return;
SDL_Quit();
sdl2Initialized_ = false;
E2D_LOG_INFO("SDL2 shutdown");
}
bool WindowModuleInitializer::initialize(const IModuleConfig* config) {
if (initialized_) return true; if (initialized_) return true;
const WindowModuleConfig* windowConfig = dynamic_cast<const WindowModuleConfig*>(config);
if (!windowConfig) {
E2D_LOG_ERROR("Invalid window module config");
return false;
}
windowConfig_ = windowConfig->windowConfig;
#ifdef __SWITCH__ #ifdef __SWITCH__
windowConfig_.mode = WindowMode::Fullscreen; cfg_.mode = WindowMode::Fullscreen;
windowConfig_.resizable = false;
windowConfig_.highDPI = false;
E2D_LOG_INFO("Switch platform: forcing fullscreen mode");
#endif #endif
if (!initSDL2()) { // 初始化SDL后端注册到工厂
return false;
}
extern void initSDL2Backend();
initSDL2Backend(); initSDL2Backend();
if (!BackendFactory::has("sdl2")) { // 初始化SDL
E2D_LOG_ERROR("SDL2 backend not registered!"); Uint32 flags = SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER;
shutdownSDL2(); #ifdef __SWITCH__
flags |= SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER;
#endif
if (SDL_Init(flags) != 0) {
E2D_LOG_ERROR("SDL_Init failed: {}", SDL_GetError());
return false;
}
sdlInited_ = true;
E2D_LOG_INFO("SDL initialized successfully");
// 创建窗口配置
WindowConfigData winCfg;
winCfg.title = cfg_.title;
winCfg.width = cfg_.w;
winCfg.height = cfg_.h;
winCfg.mode = cfg_.mode;
winCfg.vsync = cfg_.vsync;
E2D_LOG_INFO("Creating window with size {}x{}", cfg_.w, cfg_.h);
// 创建窗口(使用配置的后端)
win_ = BackendFactory::createWindow(cfg_.backend);
if (!win_) {
E2D_LOG_ERROR("Failed to create window backend");
shutdown();
return false; return false;
} }
if (!createWindow(windowConfig_)) { if (!win_->create(winCfg)) {
E2D_LOG_ERROR("Failed to create window"); E2D_LOG_ERROR("Failed to create window");
shutdownSDL2(); shutdown();
return false; return false;
} }
E2D_LOG_INFO("Window created successfully");
initialized_ = true; initialized_ = true;
E2D_LOG_INFO("Window module initialized");
E2D_LOG_INFO(" Window: {}x{}", window_->width(), window_->height());
E2D_LOG_INFO(" Backend: SDL2");
E2D_LOG_INFO(" VSync: {}", windowConfig_.vsync);
E2D_LOG_INFO(" Fullscreen: {}", windowConfig_.isFullscreen());
return true; return true;
} }
bool WindowModuleInitializer::createWindow(const WindowConfigData& config) { void WindowModule::shutdown() {
window_ = BackendFactory::createWindow("sdl2");
if (!window_) {
E2D_LOG_ERROR("Failed to create SDL2 window");
return false;
}
if (!window_->create(config)) {
E2D_LOG_ERROR("Failed to create window with specified config");
return false;
}
return true;
}
void WindowModuleInitializer::shutdown() {
if (!initialized_) return; if (!initialized_) return;
E2D_LOG_INFO("Window module shutting down"); if (win_) {
win_->destroy();
if (window_) { win_.reset();
window_->destroy();
window_.reset();
} }
shutdownSDL2(); if (sdlInited_) {
SDL_Quit();
sdlInited_ = false;
}
initialized_ = false; initialized_ = false;
} }
void register_window_module() { } // namespace extra2d
if (s_windowModuleId != INVALID_MODULE_ID) return;
s_windowModuleId = ModuleRegistry::instance().registerModule(
makeUnique<WindowModuleConfig>(),
[]() -> UniquePtr<IModuleInitializer> {
auto initializer = makeUnique<WindowModuleInitializer>();
initializer->setModuleId(s_windowModuleId);
return initializer;
}
);
}
namespace {
struct WindowModuleAutoRegister {
WindowModuleAutoRegister() {
register_window_module();
}
};
static WindowModuleAutoRegister s_autoRegister;
}
}

View File

@ -1,23 +0,0 @@
#include <extra2d/resource/resource_config.h>
#include <algorithm>
namespace extra2d {
void ResourceConfigData::addSearchPath(const std::string& path) {
if (!hasSearchPath(path)) {
searchPaths.push_back(path);
}
}
void ResourceConfigData::removeSearchPath(const std::string& path) {
auto it = std::find(searchPaths.begin(), searchPaths.end(), path);
if (it != searchPaths.end()) {
searchPaths.erase(it);
}
}
bool ResourceConfigData::hasSearchPath(const std::string& path) const {
return std::find(searchPaths.begin(), searchPaths.end(), path) != searchPaths.end();
}
}

View File

@ -1,6 +1,6 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <extra2d/graphics/render_command.h> #include <extra2d/graphics/core/render_command.h>
#include <extra2d/scene/node.h> #include <extra2d/scene/node.h>
#include <extra2d/scene/scene.h> #include <extra2d/scene/scene.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>

View File

@ -1,5 +1,5 @@
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/render_command.h> #include <extra2d/graphics/core/render_command.h>
#include <extra2d/scene/scene.h> #include <extra2d/scene/scene.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>

View File

@ -1,7 +1,7 @@
#include <algorithm> #include <algorithm>
#include <extra2d/app/application.h> #include <extra2d/app/application.h>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/render_command.h> #include <extra2d/graphics/core/render_command.h>
#include <extra2d/platform/iinput.h> #include <extra2d/platform/iinput.h>
#include <extra2d/scene/scene_manager.h> #include <extra2d/scene/scene_manager.h>
#include <extra2d/scene/transition_box_scene.h> #include <extra2d/scene/transition_box_scene.h>
@ -545,7 +545,8 @@ bool SceneManager::hasScene(const std::string &name) const {
* *
*/ */
void SceneManager::update(float dt) { void SceneManager::update(float dt) {
if (isTransitioning_) { if (isTransitioning_ && activeTransitionScene_) {
activeTransitionScene_->updateTransition(dt);
hoverTarget_ = nullptr; hoverTarget_ = nullptr;
captureTarget_ = nullptr; captureTarget_ = nullptr;
hasLastPointerWorld_ = false; hasLastPointerWorld_ = false;
@ -746,8 +747,9 @@ void SceneManager::finishTransition() {
* *
*/ */
void SceneManager::dispatchPointerEvents(Scene &scene) { void SceneManager::dispatchPointerEvents(Scene &scene) {
auto &input = Application::get().input(); auto *input = Application::get().input();
Vec2 screenPos = input.mouse(); if (!input) return;
Vec2 screenPos = input->mouse();
Vec2 worldPos = screenPos; Vec2 worldPos = screenPos;
if (auto *camera = scene.getActiveCamera()) { if (auto *camera = scene.getActiveCamera()) {
@ -784,13 +786,13 @@ void SceneManager::dispatchPointerEvents(Scene &scene) {
dispatchToNode(hoverTarget_, evt); dispatchToNode(hoverTarget_, evt);
} }
float scrollDelta = input.scrollDelta(); float scrollDelta = input->scrollDelta();
if (hoverTarget_ && scrollDelta != 0.0f) { if (hoverTarget_ && scrollDelta != 0.0f) {
Event evt = Event::createMouseScroll(Vec2(0.0f, scrollDelta), worldPos); Event evt = Event::createMouseScroll(Vec2(0.0f, scrollDelta), worldPos);
dispatchToNode(hoverTarget_, evt); dispatchToNode(hoverTarget_, evt);
} }
if (input.pressed(Mouse::Left)) { if (input->pressed(Mouse::Left)) {
captureTarget_ = hoverTarget_; captureTarget_ = hoverTarget_;
if (captureTarget_) { if (captureTarget_) {
Event evt = Event::createMouseButtonPress(static_cast<int>(Mouse::Left), Event evt = Event::createMouseButtonPress(static_cast<int>(Mouse::Left),
@ -804,7 +806,7 @@ void SceneManager::dispatchPointerEvents(Scene &scene) {
} }
} }
if (input.released(Mouse::Left)) { if (input->released(Mouse::Left)) {
Node *target = captureTarget_ ? captureTarget_ : hoverTarget_; Node *target = captureTarget_ ? captureTarget_ : hoverTarget_;
if (target) { if (target) {
Event evt = Event::createMouseButtonRelease(static_cast<int>(Mouse::Left), Event evt = Event::createMouseButtonRelease(static_cast<int>(Mouse::Left),

View File

@ -1,7 +1,7 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/render_command.h> #include <extra2d/graphics/core/render_command.h>
#include <extra2d/scene/shape_node.h> #include <extra2d/scene/shape_node.h>
#include <limits> #include <limits>

View File

@ -1,8 +1,8 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/graphics/render_command.h> #include <extra2d/graphics/core/render_command.h>
#include <extra2d/graphics/texture.h> #include <extra2d/graphics/texture/texture.h>
#include <extra2d/scene/sprite.h> #include <extra2d/scene/sprite.h>
namespace extra2d { namespace extra2d {

View File

@ -1,7 +1,8 @@
#include <extra2d/scene/transition_box_scene.h> #include <extra2d/scene/transition_box_scene.h>
#include <extra2d/app/application.h> #include <extra2d/app/application.h>
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/platform/iwindow.h>
#include <algorithm> #include <algorithm>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
@ -38,6 +39,14 @@ Ptr<TransitionBoxScene> TransitionBoxScene::create(float duration,
void TransitionBoxScene::onTransitionStart() { void TransitionBoxScene::onTransitionStart() {
} }
/**
* @brief
* @param dt
*/
void TransitionBoxScene::updateTransition(float dt) {
TransitionScene::updateTransition(dt);
}
/** /**
* @brief * @brief
* @param renderer * @param renderer
@ -46,11 +55,8 @@ void TransitionBoxScene::onTransitionStart() {
*/ */
void TransitionBoxScene::renderContent(RenderBackend &renderer) { void TransitionBoxScene::renderContent(RenderBackend &renderer) {
auto &app = Application::get(); auto &app = Application::get();
float windowWidth = static_cast<float>(app.window().width()); float windowWidth = static_cast<float>(app.window()->width());
float windowHeight = static_cast<float>(app.window().height()); float windowHeight = static_cast<float>(app.window()->height());
elapsed_ += 1.0f / 60.0f;
progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f;
if (inScene_) { if (inScene_) {
inScene_->renderContent(renderer); inScene_->renderContent(renderer);
@ -78,10 +84,6 @@ void TransitionBoxScene::renderContent(RenderBackend &renderer) {
renderer.fillRect(Rect(x * cellW, y * cellH, cellW + 1.0f, cellH + 1.0f), renderer.fillRect(Rect(x * cellW, y * cellH, cellW + 1.0f, cellH + 1.0f),
Colors::Black); Colors::Black);
} }
if (progress_ >= 1.0f && !isFinished_) {
finish();
}
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,6 +1,7 @@
#include <extra2d/scene/transition_fade_scene.h> #include <extra2d/scene/transition_fade_scene.h>
#include <extra2d/app/application.h> #include <extra2d/app/application.h>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/platform/iwindow.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
#include <algorithm> #include <algorithm>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
@ -39,30 +40,29 @@ void TransitionFadeScene::onTransitionStart() {
E2D_LOG_DEBUG("TransitionFadeScene::onTransitionStart - 启动淡入淡出过渡"); E2D_LOG_DEBUG("TransitionFadeScene::onTransitionStart - 启动淡入淡出过渡");
} }
/** void TransitionFadeScene::updateTransition(float dt) {
* @brief // 调用基类更新进度
* @param renderer TransitionScene::updateTransition(dt);
*
*
*/
void TransitionFadeScene::renderContent(RenderBackend &renderer) {
auto &app = Application::get();
float windowWidth = static_cast<float>(app.window().width());
float windowHeight = static_cast<float>(app.window().height());
elapsed_ += 1.0f / 60.0f;
progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f;
// 检查是否需要切换场景显示
if (!hasSwitched_ && progress_ >= 0.5f) { if (!hasSwitched_ && progress_ >= 0.5f) {
hideOutShowIn(); hideOutShowIn();
} }
}
void TransitionFadeScene::renderContent(RenderBackend &renderer) {
auto &app = Application::get();
float windowWidth = static_cast<float>(app.window()->width());
float windowHeight = static_cast<float>(app.window()->height());
// 根据进度选择渲染哪个场景
if (progress_ < 0.5f) { if (progress_ < 0.5f) {
drawOutScene(renderer); drawOutScene(renderer);
} else { } else {
drawInScene(renderer); drawInScene(renderer);
} }
// 计算遮罩透明度
float maskAlpha; float maskAlpha;
if (progress_ < 0.5f) { if (progress_ < 0.5f) {
maskAlpha = progress_ * 2.0f; maskAlpha = progress_ * 2.0f;
@ -80,10 +80,6 @@ void TransitionFadeScene::renderContent(RenderBackend &renderer) {
Color maskColor = maskColor_; Color maskColor = maskColor_;
maskColor.a = maskAlpha; maskColor.a = maskAlpha;
renderer.fillRect(Rect(0.0f, 0.0f, windowWidth, windowHeight), maskColor); renderer.fillRect(Rect(0.0f, 0.0f, windowWidth, windowHeight), maskColor);
if (progress_ >= 1.0f && !isFinished_) {
finish();
}
} }
/** /**

View File

@ -1,7 +1,7 @@
#include <extra2d/scene/transition_flip_scene.h> #include <extra2d/scene/transition_flip_scene.h>
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
#include <extra2d/graphics/camera.h> #include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
namespace extra2d { namespace extra2d {
@ -36,6 +36,14 @@ Ptr<TransitionFlipScene> TransitionFlipScene::create(float duration,
void TransitionFlipScene::onTransitionStart() { void TransitionFlipScene::onTransitionStart() {
} }
/**
* @brief
* @param dt
*/
void TransitionFlipScene::updateTransition(float dt) {
TransitionScene::updateTransition(dt);
}
/** /**
* @brief * @brief
* @param renderer * @param renderer
@ -43,9 +51,6 @@ void TransitionFlipScene::onTransitionStart() {
* *
*/ */
void TransitionFlipScene::renderContent(RenderBackend &renderer) { void TransitionFlipScene::renderContent(RenderBackend &renderer) {
elapsed_ += 1.0f / 60.0f;
progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f;
float easeProgress = progress_ < 0.5f ? 2.0f * progress_ * progress_ float easeProgress = progress_ < 0.5f ? 2.0f * progress_ * progress_
: -1.0f + (4.0f - 2.0f * progress_) * progress_; : -1.0f + (4.0f - 2.0f * progress_) * progress_;
@ -99,9 +104,6 @@ void TransitionFlipScene::renderContent(RenderBackend &renderer) {
} }
} }
if (progress_ >= 1.0f && !isFinished_) {
finish();
}
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,6 +1,6 @@
#include <extra2d/scene/transition_scale_scene.h> #include <extra2d/scene/transition_scale_scene.h>
#include <extra2d/graphics/camera.h> #include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <algorithm> #include <algorithm>
namespace extra2d { namespace extra2d {
@ -32,6 +32,14 @@ Ptr<TransitionScaleScene> TransitionScaleScene::create(float duration,
void TransitionScaleScene::onTransitionStart() { void TransitionScaleScene::onTransitionStart() {
} }
/**
* @brief
* @param dt
*/
void TransitionScaleScene::updateTransition(float dt) {
TransitionScene::updateTransition(dt);
}
/** /**
* @brief * @brief
* @param renderer * @param renderer
@ -39,9 +47,6 @@ void TransitionScaleScene::onTransitionStart() {
* *
*/ */
void TransitionScaleScene::renderContent(RenderBackend &renderer) { void TransitionScaleScene::renderContent(RenderBackend &renderer) {
elapsed_ += 1.0f / 60.0f;
progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f;
float easeProgress = progress_ < 0.5f ? 2.0f * progress_ * progress_ float easeProgress = progress_ < 0.5f ? 2.0f * progress_ * progress_
: -1.0f + (4.0f - 2.0f * progress_) * progress_; : -1.0f + (4.0f - 2.0f * progress_) * progress_;
@ -79,9 +84,6 @@ void TransitionScaleScene::renderContent(RenderBackend &renderer) {
} }
} }
if (progress_ >= 1.0f && !isFinished_) {
finish();
}
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,5 +1,5 @@
#include <extra2d/scene/transition_scene.h> #include <extra2d/scene/transition_scene.h>
#include <extra2d/graphics/render_backend.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
namespace extra2d { namespace extra2d {
@ -50,13 +50,32 @@ void TransitionScene::onExit() {
Scene::onExit(); Scene::onExit();
} }
/**
* @brief
* @param dt
*
*
*/
void TransitionScene::updateTransition(float dt) {
if (isFinished_ || isCancelled_) {
return;
}
elapsed_ += dt;
progress_ = duration_ > 0.0f ? std::min(1.0f, elapsed_ / duration_) : 1.0f;
if (progress_ >= 1.0f) {
finish();
}
}
/** /**
* @brief * @brief
* *
* *
*/ */
void TransitionScene::finish() { void TransitionScene::finish() {
if (isFinished_) { if (isFinished_ || isCancelled_) {
return; return;
} }
@ -69,6 +88,30 @@ void TransitionScene::finish() {
} }
} }
/**
* @brief
* @param immediate false则回滚到原场景
*/
void TransitionScene::cancel(bool immediate) {
if (isFinished_ || isCancelled_) {
return;
}
isCancelled_ = true;
if (immediate) {
// 立即完成,切换到新场景
E2D_LOG_DEBUG("TransitionScene::cancel - 立即完成过渡");
finish();
} else {
// 回滚到原场景
E2D_LOG_DEBUG("TransitionScene::cancel - 取消过渡,回滚到原场景");
if (cancelCallback_) {
cancelCallback_();
}
}
}
/** /**
* @brief * @brief
* @param renderer * @param renderer

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