Compare commits

...

2 Commits

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

View File

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

View File

@ -1,199 +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
*/
/**
* @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,280 +0,0 @@
#pragma once
#include <extra2d/config/app_config.h>
#include <extra2d/config/config_loader.h>
#include <extra2d/core/types.h>
#include <mutex>
#include <string>
#include <unordered_map>
namespace extra2d {
/**
* @file config_manager.h
* @brief
*
* AppConfig
*/
/**
* @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
* @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<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,205 +1,76 @@
#pragma once #pragma once
#include <extra2d/core/export.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <string>
#include <vector> #include <vector>
#include <typeindex>
namespace extra2d { namespace extra2d {
class Module; class Application;
class UpdateContext;
class RenderContext;
/**
* @brief
*
*/
class E2D_API ModuleContext {
public:
/**
* @brief
*/
virtual ~ModuleContext() = default;
/**
* @brief
*/
void next();
/**
* @brief
* @return true
*/
bool isDone() const { return index_ >= static_cast<int>(modules_.size()); }
protected:
/**
* @brief
* @param modules
*/
ModuleContext(std::vector<Module*>& modules);
/**
* @brief
* @param m
*/
virtual void handle(Module* m) = 0;
std::vector<Module*>& modules_;
int index_ = 0;
};
/**
* @brief
*
*/
class UpdateContext : public ModuleContext {
public:
/**
* @brief
* @param modules
* @param deltaTime
*/
UpdateContext(std::vector<Module*>& modules, float deltaTime);
/**
* @brief
* @return
*/
float getDeltaTime() const { return deltaTime_; }
protected:
void handle(Module* m) override;
private:
float deltaTime_;
};
/**
* @brief
*
*/
class RenderContext : public ModuleContext {
public:
/**
* @brief
*/
enum class Phase {
Before, ///< 渲染前
On, ///< 渲染时
After ///< 渲染后
};
/**
* @brief
* @param modules
* @param phase
*/
RenderContext(std::vector<Module*>& modules, Phase phase);
/**
* @brief
* @return
*/
Phase getPhase() const { return phase_; }
protected:
void handle(Module* m) override;
private:
Phase phase_;
};
/** /**
* @brief * @brief
* *
*/ */
class Module { class Module {
public: public:
/**
* @brief
*/
virtual ~Module() = default; virtual ~Module() = default;
/** /**
* @brief * @brief
* Application::run() * @return true
*/ */
virtual void setupModule() {} virtual bool init() = 0;
/** /**
* @brief * @brief
* Application
*/ */
virtual void destroyModule() {} virtual void shutdown() = 0;
/**
* @brief
*
* @param ctx
*/
virtual void onUpdate(UpdateContext& ctx) { ctx.next(); }
/**
* @brief
*
* @param ctx
*/
virtual void beforeRender(RenderContext& ctx) { ctx.next(); }
/**
* @brief
*
* @param ctx
*/
virtual void onRender(RenderContext& ctx) { ctx.next(); }
/**
* @brief
*
* @param ctx
*/
virtual void afterRender(RenderContext& ctx) { ctx.next(); }
/**
* @brief
* @return
*/
virtual const char* getName() const = 0;
/**
* @brief
*
* @return
*/
virtual int getPriority() const { return 0; }
/** /**
* @brief * @brief
* @return true * @return true
*/ */
bool isInitialized() const { return initialized_; } 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: protected:
friend class Application; class Application* app_ = nullptr;
/**
* @brief
* @param initialized
*/
void setInitialized(bool initialized) { initialized_ = initialized; }
bool initialized_ = false;
}; };
/** /**
* @brief * @brief
*
* @param a a
* @param b b
* @return a优先级小于b返回true
*/ */
inline bool modulePriorityCompare(Module* a, Module* b) { using ModuleFactory = std::function<UniquePtr<Module>()>;
return a->getPriority() < b->getPriority();
}
} // namespace extra2d } // 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

@ -6,39 +6,34 @@
// Core // Core
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/math_types.h> #include <extra2d/core/math_types.h>
#include <extra2d/core/module.h>
#include <extra2d/core/module_macros.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/platform_config.h>
#include <extra2d/config/config_manager.h> #include <extra2d/config/platform_detector.h>
// Modules
#include <extra2d/modules/config_module.h>
#include <extra2d/modules/input_module.h>
#include <extra2d/modules/render_module.h>
#include <extra2d/modules/window_module.h>
// Platform // Platform
#include <extra2d/platform/iinput.h> #include <extra2d/platform/iinput.h>
#include <extra2d/platform/iwindow.h> #include <extra2d/platform/iwindow.h>
#include <extra2d/platform/keys.h> #include <extra2d/platform/keys.h>
#include <extra2d/platform/window_config.h> #include <extra2d/platform/input_module.h>
#include <extra2d/platform/backend_factory.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>
@ -49,17 +44,19 @@
// Event // Event
#include <extra2d/event/event.h> #include <extra2d/event/event.h>
#include <extra2d/event/event_dispatcher.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>
// Services // Services
#include <extra2d/services/camera_service.h>
#include <extra2d/services/event_service.h> #include <extra2d/services/event_service.h>
#include <extra2d/services/scene_service.h> #include <extra2d/services/scene_service.h>
#include <extra2d/services/timer_service.h> #include <extra2d/services/timer_service.h>
#include <extra2d/services/camera_service.h>
#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

@ -0,0 +1,131 @@
#pragma once
#include <extra2d/core/types.h>
#include <string>
#include <unordered_map>
#include <vector>
namespace extra2d {
// ============================================================================
// Shader缓存条目
// ============================================================================
struct ShaderCacheEntry {
std::string name;
std::string sourceHash;
uint64_t compileTime = 0;
std::vector<uint8_t> binary;
std::vector<std::string> dependencies;
};
// ============================================================================
// Shader缓存管理器
// ============================================================================
class ShaderCache {
public:
/**
* @brief
* @return
*/
static ShaderCache& getInstance();
/**
* @brief
* @param cacheDir
* @return truefalse
*/
bool init(const std::string& cacheDir);
/**
* @brief
*/
void shutdown();
/**
* @brief
* @param name Shader名称
* @param sourceHash
* @return truefalse
*/
bool hasValidCache(const std::string& name, const std::string& sourceHash);
/**
* @brief
* @param name Shader名称
* @return nullptr
*/
Ptr<ShaderCacheEntry> loadCache(const std::string& name);
/**
* @brief
* @param entry
* @return truefalse
*/
bool saveCache(const ShaderCacheEntry& entry);
/**
* @brief 使
* @param name Shader名称
*/
void invalidate(const std::string& name);
/**
* @brief
*/
void clearAll();
/**
* @brief
* @param vertSource
* @param fragSource
* @return
*/
static std::string computeHash(const std::string& vertSource,
const std::string& fragSource);
/**
* @brief
* @return truefalse
*/
bool isInitialized() const { return initialized_; }
private:
ShaderCache() = default;
~ShaderCache() = default;
ShaderCache(const ShaderCache&) = delete;
ShaderCache& operator=(const ShaderCache&) = delete;
std::string cacheDir_;
std::unordered_map<std::string, ShaderCacheEntry> cacheMap_;
bool initialized_ = false;
/**
* @brief
* @return truefalse
*/
bool loadCacheIndex();
/**
* @brief
* @return truefalse
*/
bool saveCacheIndex();
/**
* @brief
* @param name Shader名称
* @return
*/
std::string getCachePath(const std::string& name) const;
/**
* @brief
* @return truefalse
*/
bool ensureCacheDirectory();
};
// 便捷宏
#define E2D_SHADER_CACHE() ::extra2d::ShaderCache::getInstance()
} // namespace extra2d

View File

@ -0,0 +1,137 @@
#pragma once
#include <extra2d/core/types.h>
#include <functional>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#endif
namespace extra2d {
// ============================================================================
// 文件变化事件
// ============================================================================
struct FileChangeEvent {
std::string filepath;
enum class Type {
Created,
Modified,
Deleted,
Renamed
} type;
uint64_t timestamp = 0;
};
// ============================================================================
// 文件变化回调
// ============================================================================
using FileChangeCallback = std::function<void(const FileChangeEvent&)>;
// ============================================================================
// Shader热重载管理器
// ============================================================================
class ShaderHotReloader {
public:
/**
* @brief
* @return
*/
static ShaderHotReloader& getInstance();
/**
* @brief
* @return truefalse
*/
bool init();
/**
* @brief
*/
void shutdown();
/**
* @brief Shader文件监视
* @param shaderName Shader名称
* @param filePaths
* @param callback
*/
void watch(const std::string& shaderName,
const std::vector<std::string>& filePaths,
FileChangeCallback callback);
/**
* @brief
* @param shaderName Shader名称
*/
void unwatch(const std::string& shaderName);
/**
* @brief
*/
void update();
/**
* @brief /
* @param enabled
*/
void setEnabled(bool enabled);
/**
* @brief
* @return truefalse
*/
bool isEnabled() const { return enabled_; }
/**
* @brief
* @return truefalse
*/
bool isInitialized() const { return initialized_; }
private:
ShaderHotReloader() = default;
~ShaderHotReloader() = default;
ShaderHotReloader(const ShaderHotReloader&) = delete;
ShaderHotReloader& operator=(const ShaderHotReloader&) = delete;
bool enabled_ = false;
bool initialized_ = false;
struct WatchInfo {
std::vector<std::string> filePaths;
FileChangeCallback callback;
std::unordered_map<std::string, uint64_t> modifiedTimes;
};
std::unordered_map<std::string, WatchInfo> watchMap_;
#ifdef _WIN32
HANDLE watchHandle_ = nullptr;
std::vector<uint8_t> buffer_;
std::string watchDir_;
bool watching_ = false;
#endif
/**
* @brief
*/
void pollChanges();
/**
* @brief
* @param filepath
* @return
*/
static uint64_t getFileModifiedTime(const std::string& filepath);
};
// 便捷宏
#define E2D_SHADER_HOT_RELOADER() ::extra2d::ShaderHotReloader::getInstance()
} // namespace extra2d

View File

@ -1,52 +1,22 @@
#pragma once #pragma once
#include <extra2d/graphics/shader_interface.h> #include <extra2d/config/platform_detector.h>
#include <extra2d/graphics/shader_loader.h> #include <extra2d/graphics/shader/shader_cache.h>
#include <extra2d/graphics/shader/shader_hot_reloader.h>
#include <extra2d/graphics/shader/shader_interface.h>
#include <extra2d/graphics/shader/shader_loader.h>
#include <functional> #include <functional>
#include <unordered_map> #include <unordered_map>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#endif
namespace extra2d { namespace extra2d {
// ============================================================================
// Shader缓存条目
// ============================================================================
struct ShaderCacheEntry {
std::string name;
std::string sourceHash;
uint64_t compileTime = 0;
std::vector<uint8_t> binary;
std::vector<std::string> dependencies;
};
// ============================================================================
// 文件变化事件
// ============================================================================
struct FileChangeEvent {
std::string filepath;
enum class Type {
Created,
Modified,
Deleted,
Renamed
} type;
uint64_t timestamp = 0;
};
// ============================================================================ // ============================================================================
// Shader重载回调 // Shader重载回调
// ============================================================================ // ============================================================================
using ShaderReloadCallback = std::function<void(Ptr<IShader> newShader)>; using ShaderReloadCallback = std::function<void(Ptr<IShader> newShader)>;
using FileChangeCallback = std::function<void(const FileChangeEvent&)>;
// ============================================================================ // ============================================================================
// Shader管理器 - 统一入口(加载/缓存/热重载) // Shader管理器 - 统一入口
// ============================================================================ // ============================================================================
class ShaderManager { class ShaderManager {
public: public:
@ -62,6 +32,7 @@ public:
/** /**
* @brief 使Shader系统 * @brief 使Shader系统
* 使romfs/sdmc/
* @param factory Shader工厂 * @param factory Shader工厂
* @param appName * @param appName
* @return truefalse * @return truefalse
@ -86,11 +57,14 @@ public:
/** /**
* @brief * @brief
* @return truefalse
*/ */
bool isInitialized() const { return initialized_; } bool isInitialized() const { return initialized_; }
/** /**
* @brief * @brief
* Switch平台使用romfs
* @return true
*/ */
bool isHotReloadSupported() const { return hotReloadSupported_; } bool isHotReloadSupported() const { return hotReloadSupported_; }
@ -98,73 +72,134 @@ public:
// Shader加载 // Shader加载
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/**
* @brief Shader
* @param name Shader名称
* @param vertPath
* @param fragPath
* @return Shader实例
*/
Ptr<IShader> loadFromFiles(const std::string& name, Ptr<IShader> loadFromFiles(const std::string& name,
const std::string& vertPath, const std::string& vertPath,
const std::string& fragPath); const std::string& fragPath);
/**
* @brief Shader
* @param path Shader文件路径
* @return Shader实例
*/
Ptr<IShader> loadFromCombinedFile(const std::string& path); Ptr<IShader> loadFromCombinedFile(const std::string& path);
/**
* @brief Shader
* @param name Shader名称
* @param vertSource
* @param fragSource
* @return Shader实例
*/
Ptr<IShader> loadFromSource(const std::string& name, Ptr<IShader> loadFromSource(const std::string& name,
const std::string& vertSource, const std::string& vertSource,
const std::string& fragSource); const std::string& fragSource);
/**
* @brief Shader
* @param name Shader名称
* @return Shader实例nullptr
*/
Ptr<IShader> get(const std::string& name) const; Ptr<IShader> get(const std::string& name) const;
/**
* @brief Shader是否存在
* @param name Shader名称
* @return truefalse
*/
bool has(const std::string& name) const; bool has(const std::string& name) const;
/**
* @brief Shader
* @param name Shader名称
*/
void remove(const std::string& name); void remove(const std::string& name);
/**
* @brief Shader
*/
void clear(); void clear();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 热重载 // 热重载
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/**
* @brief
* @param name Shader名称
* @param callback
*/
void setReloadCallback(const std::string& name, ShaderReloadCallback callback); void setReloadCallback(const std::string& name, ShaderReloadCallback callback);
/**
* @brief /
* @param enabled
*/
void setHotReloadEnabled(bool enabled); void setHotReloadEnabled(bool enabled);
/**
* @brief
* @return truefalse
*/
bool isHotReloadEnabled() const; bool isHotReloadEnabled() const;
/**
* @brief
*/
void update(); void update();
/**
* @brief Shader
* @param name Shader名称
* @return truefalse
*/
bool reload(const std::string& name); bool reload(const std::string& name);
// ------------------------------------------------------------------------
// 热重载文件监视
// ------------------------------------------------------------------------
void watch(const std::string& shaderName,
const std::vector<std::string>& filePaths,
FileChangeCallback callback);
void unwatch(const std::string& shaderName);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 内置Shader // 内置Shader
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/**
* @brief Shader
* @param name Shader名称
* @return Shader实例
*/
Ptr<IShader> getBuiltin(const std::string& name); Ptr<IShader> getBuiltin(const std::string& name);
/**
* @brief Shader
* @return truefalse
*/
bool loadBuiltinShaders(); bool loadBuiltinShaders();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// 工具方法 // 工具方法
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/**
* @brief Shader目录
* @return Shader目录路径
*/
const std::string& getShaderDir() const { return shaderDir_; } const std::string& getShaderDir() const { return shaderDir_; }
/**
* @brief ShaderLoader
* @return ShaderLoader引用
*/
ShaderLoader& getLoader() { return loader_; } ShaderLoader& getLoader() { return loader_; }
// ------------------------------------------------------------------------
// 缓存
// ------------------------------------------------------------------------
bool hasValidCache(const std::string& name, const std::string& sourceHash);
Ptr<ShaderCacheEntry> loadCache(const std::string& name);
bool saveCache(const ShaderCacheEntry& entry);
void invalidateCache(const std::string& name);
void clearAllCache();
static std::string computeHash(const std::string& vertSource,
const std::string& fragSource);
private: private:
ShaderManager() = default; ShaderManager() = default;
~ShaderManager() = default; ~ShaderManager() = default;
ShaderManager(const ShaderManager&) = delete; ShaderManager(const ShaderManager&) = delete;
ShaderManager& operator=(const ShaderManager&) = delete; ShaderManager& operator=(const ShaderManager&) = delete;
// Shader存储
std::string shaderDir_; std::string shaderDir_;
std::string cacheDir_; std::string cacheDir_;
Ptr<IShaderFactory> factory_; Ptr<IShaderFactory> factory_;
@ -184,45 +219,29 @@ private:
bool hotReloadEnabled_ = false; bool hotReloadEnabled_ = false;
bool hotReloadSupported_ = true; bool hotReloadSupported_ = true;
// 缓存(原 ShaderCache /**
std::unordered_map<std::string, ShaderCacheEntry> cacheMap_; * @brief Shader
bool cacheInitialized_ = false; * @param name Shader名称
* @param sourceHash
bool initCache(const std::string& cacheDir); * @param vertSource
void shutdownCache(); * @param fragSource
bool loadCacheIndex(); * @return Shader实例
bool saveCacheIndex(); */
std::string getCachePath(const std::string& name) const;
bool ensureCacheDirectory();
// 热重载(原 ShaderHotReloader
bool reloaderInitialized_ = false;
struct WatchInfo {
std::vector<std::string> filePaths;
FileChangeCallback callback;
std::unordered_map<std::string, uint64_t> modifiedTimes;
};
std::unordered_map<std::string, WatchInfo> watchMap_;
#ifdef _WIN32
HANDLE watchHandle_ = nullptr;
std::vector<uint8_t> watchBuffer_;
std::string watchDir_;
bool watching_ = false;
#endif
bool initReloader();
void shutdownReloader();
void pollChanges();
static uint64_t getFileModifiedTime(const std::string& filepath);
Ptr<IShader> loadFromCache(const std::string& name, Ptr<IShader> loadFromCache(const std::string& name,
const std::string& sourceHash, const std::string& sourceHash,
const std::string& vertSource, const std::string& vertSource,
const std::string& fragSource); const std::string& fragSource);
/**
* @brief Shader源码
*/
void createBuiltinShaderSources(); void createBuiltinShaderSources();
/**
* @brief
* @param shaderName Shader名称
* @param event
*/
void handleFileChange(const std::string& shaderName, const FileChangeEvent& event); void handleFileChange(const std::string& shaderName, const FileChangeEvent& event);
}; };

View File

@ -0,0 +1,112 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/types.h>
#include <extra2d/graphics/shader/shader_interface.h>
#include <glm/vec4.hpp>
namespace extra2d {
struct WaterParams {
float waveSpeed = 1.0f;
float waveAmplitude = 0.02f;
float waveFrequency = 4.0f;
};
struct OutlineParams {
Color color = Colors::Black;
float thickness = 2.0f;
};
struct DistortionParams {
float distortionAmount = 0.02f;
float timeScale = 1.0f;
};
struct PixelateParams {
float pixelSize = 8.0f;
};
struct InvertParams {
float strength = 1.0f;
};
struct GrayscaleParams {
float intensity = 1.0f;
};
struct BlurParams {
float radius = 5.0f;
};
class ShaderPreset {
public:
/**
* @brief
* @param params
* @return
*/
static Ptr<IShader> Water(const WaterParams& params = {});
/**
* @brief
* @param params
* @return
*/
static Ptr<IShader> Outline(const OutlineParams& params = {});
/**
* @brief
* @param params
* @return
*/
static Ptr<IShader> Distortion(const DistortionParams& params = {});
/**
* @brief
* @param params
* @return
*/
static Ptr<IShader> Pixelate(const PixelateParams& params = {});
/**
* @brief
* @param params
* @return
*/
static Ptr<IShader> Invert(const InvertParams& params = {});
/**
* @brief
* @param params
* @return
*/
static Ptr<IShader> Grayscale(const GrayscaleParams& params = {});
/**
* @brief
* @param params
* @return
*/
static Ptr<IShader> Blur(const BlurParams& params = {});
/**
* @brief +
* @param grayParams
* @param outlineParams
* @return
*/
static Ptr<IShader> GrayscaleOutline(const GrayscaleParams& grayParams,
const OutlineParams& outlineParams);
/**
* @brief +
* @param pixParams
* @param invParams
* @return
*/
static Ptr<IShader> PixelateInvert(const PixelateParams& pixParams,
const InvertParams& invParams);
};
} // namespace extra2d

View File

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

@ -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/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,8 +1,7 @@
#pragma once #pragma once
#include <extra2d/core/color.h> #include <extra2d/core/color.h>
#include <extra2d/core/export.h> #include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/camera.h>
#include <extra2d/scene/node.h> #include <extra2d/scene/node.h>
#include <vector> #include <vector>
@ -14,7 +13,7 @@ struct RenderCommand;
// ============================================================================ // ============================================================================
// 场景类 - 节点容器,管理整个场景图 // 场景类 - 节点容器,管理整个场景图
// ============================================================================ // ============================================================================
class E2D_API Scene : public Node { class Scene : public Node {
public: public:
Scene(); Scene();
~Scene() override = default; ~Scene() override = default;

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,202 +1,12 @@
#pragma once #pragma once
#include <cstdio> /**
#include <extra2d/core/export.h> * @file logger.h
#include <extra2d/core/types.h> * @brief
#include <sstream> *
#include <string> * 便 logger_service.h
#include <type_traits> */
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 E2D_API 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,366 +1,268 @@
#include <extra2d/app/application.h> #include <extra2d/app/application.h>
#include <extra2d/core/module_meta.h> #include <extra2d/core/registry.h>
#include <extra2d/graphics/vram_manager.h> #include <extra2d/platform/window_module.h>
#include <extra2d/modules/config_module.h> #include <extra2d/platform/input_module.h>
#include <extra2d/modules/input_module.h> #include <extra2d/graphics/core/render_module.h>
#include <extra2d/modules/render_module.h> #include <extra2d/platform/iwindow.h>
#include <extra2d/modules/window_module.h> #include <extra2d/platform/iinput.h>
#include <extra2d/services/camera_service.h> #include <extra2d/graphics/core/render_backend.h>
#include <extra2d/services/event_service.h>
#include <extra2d/services/scene_service.h> #include <extra2d/services/scene_service.h>
#include <extra2d/services/timer_service.h> #include <extra2d/services/timer_service.h>
#include <extra2d/utils/logger.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>
namespace extra2d { 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;
}
// 先注册核心服务,因为模块初始化时可能需要它们
registerCoreServices();
if (!initModules(config)) {
return false;
}
auto windowModule = getModule<WindowModule>();
if (!windowModule || !windowModule->isInitialized()) {
E2D_LOG_ERROR("Window module not initialized");
return false;
}
window_ = windowModule->getWindow();
if (!window_) {
E2D_LOG_ERROR("Window not created");
return false;
}
auto inputModule = getModule<InputModule>();
if (inputModule) {
inputModule->setWindow(window_);
}
auto renderModule = getModule<RenderModule>();
if (renderModule) {
renderModule->setWindow(window_);
}
// 注册 CameraService需要 window
registerCameraService();
if (!ServiceLocator::instance().initializeAll()) {
return false;
}
auto cameraService = ServiceLocator::instance().getService<ICameraService>();
if (cameraService && window_) {
window_->onResize([cameraService](int width, int height) {
cameraService->updateViewport(width, height);
cameraService->applyViewportAdapter();
auto sceneService =
ServiceLocator::instance().getService<ISceneService>();
if (sceneService) {
auto currentScene = sceneService->getCurrentScene();
if (currentScene) {
currentScene->setViewportSize(static_cast<float>(width),
static_cast<float>(height));
}
}
});
}
initialized_ = true;
running_ = true;
return true;
}
bool Application::init(const std::string &configPath) {
if (initialized_) {
return true;
}
auto configModule = getModule<ConfigModule>();
if (configModule) {
configModule->setConfigPath(configPath);
}
AppConfig cfg;
return init(cfg);
}
bool Application::initModules(const AppConfig &config) {
auto configModule = getModule<ConfigModule>();
if (configModule) {
configModule->setAppConfig(config);
}
if (!ModuleRegistry::instance().createAndInitAll()) {
E2D_LOG_ERROR("Failed to initialize modules");
return false;
}
return true;
}
void Application::registerCoreServices() {
auto &locator = ServiceLocator::instance();
if (!locator.hasService<IEventService>()) {
locator.registerService<IEventService>(makeShared<EventService>());
}
if (!locator.hasService<ISceneService>()) {
locator.registerService<ISceneService>(makeShared<SceneService>());
}
if (!locator.hasService<ITimerService>()) {
locator.registerService<ITimerService>(makeShared<TimerService>());
}
}
void Application::registerCameraService() {
auto &locator = ServiceLocator::instance();
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);
}
}
void Application::shutdown() {
if (!initialized_)
return;
E2D_LOG_INFO("Application shutting down...");
VRAMMgr::get().printStats();
// 先销毁模块(它们可能需要服务和窗口)
ModuleRegistry::instance().destroyAll();
// 最后清除服务
ServiceLocator::instance().clear();
initialized_ = false;
running_ = false;
E2D_LOG_INFO("Application shutdown complete");
} }
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();
}
// 主循环结束后自动清理
shutdown();
} }
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();
}
ConfigManager::instance().update(deltaTime_);
render();
// 帧率限制
auto* renderMod = get<RenderModule>();
if (renderMod && renderMod->renderer()) {
// 这里可以添加帧率限制逻辑
}
} }
void Application::update() { void Application::update() {
ServiceLocator::instance().updateAll(deltaTime_); ServiceLocator::instance().updateAll(deltaTime_);
auto modules = ModuleRegistry::instance().getAllModules(); auto* inputMod = get<InputModule>();
auto ctx = UpdateContext(modules, deltaTime_); if (inputMod) {
if (!modules.empty()) { inputMod->update();
ctx.next(); }
}
} }
void Application::render() { void Application::render() {
RenderBackend *renderer = nullptr; auto* renderMod = get<RenderModule>();
auto renderModule = getModule<RenderModule>(); if (!renderMod || !renderMod->renderer()) return;
if (renderModule) {
renderer = renderModule->getRenderer(); auto* renderer = renderMod->renderer();
} auto* winMod = get<WindowModule>();
if (!winMod || !winMod->win()) return;
if (!renderer) {
return; auto cameraService = ServiceLocator::instance().getService<ICameraService>();
} if (cameraService) {
const auto& vp = cameraService->getViewportResult().viewport;
auto cameraService = ServiceLocator::instance().getService<ICameraService>(); renderer->setViewport(
if (cameraService) { static_cast<int>(vp.origin.x), static_cast<int>(vp.origin.y),
const auto &vp = cameraService->getViewportResult().viewport; static_cast<int>(vp.size.width), static_cast<int>(vp.size.height));
renderer->setViewport( renderer->setViewProjection(cameraService->getViewProjectionMatrix());
static_cast<int>(vp.origin.x), static_cast<int>(vp.origin.y), } else {
static_cast<int>(vp.size.width), static_cast<int>(vp.size.height)); renderer->setViewport(0, 0, winMod->win()->width(), winMod->win()->height());
renderer->setViewProjection(cameraService->getViewProjectionMatrix());
} else {
renderer->setViewport(0, 0, window_->width(), window_->height());
}
auto modules = ModuleRegistry::instance().getAllModules();
{
auto ctx = RenderContext(modules, RenderContext::Phase::Before);
if (!modules.empty()) {
ctx.next();
} }
}
auto sceneService = ServiceLocator::instance().getService<ISceneService>();
{ if (sceneService) {
auto ctx = RenderContext(modules, RenderContext::Phase::On); sceneService->render(*renderer);
if (!modules.empty()) {
ctx.next();
} }
}
winMod->win()->swap();
auto sceneService = ServiceLocator::instance().getService<ISceneService>();
if (sceneService) {
sceneService->render(*renderer);
}
{
auto ctx = RenderContext(modules, RenderContext::Phase::After);
if (!modules.empty()) {
ctx.next();
}
}
} }
IInput &Application::input() { return *window_->input(); } IWindow* Application::window() {
auto* winMod = get<WindowModule>();
RenderBackend &Application::renderer() { return winMod ? winMod->win() : nullptr;
auto renderModule = getModule<RenderModule>();
if (renderModule && renderModule->getRenderer()) {
return *renderModule->getRenderer();
}
E2D_LOG_ERROR("RenderModule not initialized - renderer() called too early");
std::abort();
} }
SharedPtr<ISceneService> Application::scenes() { RenderBackend* Application::renderer() {
return ServiceLocator::instance().getService<ISceneService>(); auto* renderMod = get<RenderModule>();
return renderMod ? renderMod->renderer() : nullptr;
} }
SharedPtr<ITimerService> Application::timers() { IInput* Application::input() {
return ServiceLocator::instance().getService<ITimerService>(); auto* winMod = get<WindowModule>();
} return (winMod && winMod->win()) ? winMod->win()->input() : nullptr;
SharedPtr<IEventService> Application::events() {
return ServiceLocator::instance().getService<IEventService>();
}
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,289 +0,0 @@
#include <extra2d/config/config_loader.h>
#include <extra2d/config/config_manager.h>
#include <extra2d/utils/logger.h>
#include <cctype>
#include <fstream>
#include <map>
#include <sstream>
namespace extra2d {
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);
}
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;
}
using IniData = std::map<std::string, std::map<std::string, std::string>>;
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();
}
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;
}
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");
}
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";
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;
}
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());
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,147 +0,0 @@
#include <extra2d/config/config_loader.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>();
}
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;
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));
}
E2D_LOG_INFO("完整配置加载成功");
return ConfigLoadResult::ok();
}
ConfigSaveResult JsonConfigLoader::saveWithModules(const std::string& filepath) {
E2D_LOG_INFO("正在保存完整配置到 JSON 文件: {}", filepath);
json root;
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,366 +0,0 @@
#include <extra2d/config/config_manager.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_loader = makeUnique<JsonConfigLoader>();
if (!m_loader) {
E2D_LOG_ERROR("Failed to create config loader");
return false;
}
m_appConfig = AppConfig::createDefault();
m_initialized = true;
m_modified = false;
E2D_LOG_INFO("ConfigManager initialized");
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_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");
}
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

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

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

@ -0,0 +1,286 @@
#include <extra2d/graphics/shader/shader_cache.h>
#include <extra2d/utils/logger.h>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <sstream>
namespace extra2d {
namespace fs = std::filesystem;
/**
* @brief
* @return
*/
ShaderCache& ShaderCache::getInstance() {
static ShaderCache instance;
return instance;
}
/**
* @brief
* @param cacheDir
* @return truefalse
*/
bool ShaderCache::init(const std::string& cacheDir) {
cacheDir_ = cacheDir;
if (!ensureCacheDirectory()) {
E2D_LOG_ERROR("Failed to create cache directory: {}", cacheDir);
return false;
}
if (!loadCacheIndex()) {
E2D_LOG_WARN("Failed to load cache index, starting fresh");
}
initialized_ = true;
E2D_LOG_INFO("Shader cache initialized at: {}", cacheDir);
return true;
}
/**
* @brief
*/
void ShaderCache::shutdown() {
if (!initialized_) {
return;
}
saveCacheIndex();
cacheMap_.clear();
initialized_ = false;
E2D_LOG_INFO("Shader cache shutdown");
}
/**
* @brief
* @param name Shader名称
* @param sourceHash
* @return truefalse
*/
bool ShaderCache::hasValidCache(const std::string& name, const std::string& sourceHash) {
auto it = cacheMap_.find(name);
if (it == cacheMap_.end()) {
return false;
}
return it->second.sourceHash == sourceHash;
}
/**
* @brief
* @param name Shader名称
* @return nullptr
*/
Ptr<ShaderCacheEntry> ShaderCache::loadCache(const std::string& name) {
auto it = cacheMap_.find(name);
if (it == cacheMap_.end()) {
return nullptr;
}
std::string cachePath = getCachePath(name);
std::ifstream file(cachePath, std::ios::binary);
if (!file.is_open()) {
E2D_LOG_WARN("Failed to open cache file: {}", cachePath);
return nullptr;
}
auto entry = std::make_shared<ShaderCacheEntry>(it->second);
entry->binary.clear();
file.seekg(0, std::ios::end);
size_t fileSize = static_cast<size_t>(file.tellg());
file.seekg(0, std::ios::beg);
entry->binary.resize(fileSize);
file.read(reinterpret_cast<char*>(entry->binary.data()), fileSize);
return entry;
}
/**
* @brief
* @param entry
* @return truefalse
*/
bool ShaderCache::saveCache(const ShaderCacheEntry& entry) {
if (!initialized_) {
E2D_LOG_WARN("ShaderCache not initialized, cannot save cache");
return false;
}
if (entry.binary.empty()) {
E2D_LOG_WARN("Shader binary is empty, skipping cache save for: {}", entry.name);
return false;
}
std::string cachePath = getCachePath(entry.name);
E2D_LOG_DEBUG("Saving shader cache to: {} ({} bytes)", cachePath, entry.binary.size());
std::ofstream file(cachePath, std::ios::binary);
if (!file.is_open()) {
E2D_LOG_ERROR("Failed to create cache file: {}", cachePath);
return false;
}
file.write(reinterpret_cast<const char*>(entry.binary.data()), entry.binary.size());
file.close();
cacheMap_[entry.name] = entry;
saveCacheIndex();
E2D_LOG_INFO("Shader cache saved: {} ({} bytes)", entry.name, entry.binary.size());
return true;
}
/**
* @brief 使
* @param name Shader名称
*/
void ShaderCache::invalidate(const std::string& name) {
auto it = cacheMap_.find(name);
if (it == cacheMap_.end()) {
return;
}
std::string cachePath = getCachePath(name);
fs::remove(cachePath);
cacheMap_.erase(it);
saveCacheIndex();
E2D_LOG_DEBUG("Shader cache invalidated: {}", name);
}
/**
* @brief
*/
void ShaderCache::clearAll() {
for (const auto& pair : cacheMap_) {
std::string cachePath = getCachePath(pair.first);
fs::remove(cachePath);
}
cacheMap_.clear();
saveCacheIndex();
E2D_LOG_INFO("All shader caches cleared");
}
/**
* @brief
* @param vertSource
* @param fragSource
* @return
*/
std::string ShaderCache::computeHash(const std::string& vertSource,
const std::string& fragSource) {
std::string combined = vertSource + fragSource;
uint32_t hash = 5381;
for (char c : combined) {
hash = ((hash << 5) + hash) + static_cast<uint32_t>(c);
}
std::stringstream ss;
ss << std::hex << hash;
return ss.str();
}
/**
* @brief
* @return truefalse
*/
bool ShaderCache::loadCacheIndex() {
std::string indexPath = cacheDir_ + "/.cache_index";
if (!fs::exists(indexPath)) {
return true;
}
std::ifstream file(indexPath);
if (!file.is_open()) {
return false;
}
std::string line;
while (std::getline(file, line)) {
if (line.empty() || line[0] == '#') {
continue;
}
size_t pos = line.find('=');
if (pos == std::string::npos) {
continue;
}
std::string name = line.substr(0, pos);
std::string hash = line.substr(pos + 1);
std::string cachePath = getCachePath(name);
if (fs::exists(cachePath)) {
ShaderCacheEntry entry;
entry.name = name;
entry.sourceHash = hash;
entry.compileTime = static_cast<uint64_t>(
std::chrono::system_clock::now().time_since_epoch().count());
cacheMap_[name] = entry;
}
}
return true;
}
/**
* @brief
* @return truefalse
*/
bool ShaderCache::saveCacheIndex() {
std::string indexPath = cacheDir_ + "/.cache_index";
std::ofstream file(indexPath);
if (!file.is_open()) {
return false;
}
file << "# Extra2D Shader Cache Index\n";
file << "# Format: name=hash\n";
for (const auto& pair : cacheMap_) {
file << pair.first << "=" << pair.second.sourceHash << "\n";
}
return true;
}
/**
* @brief
* @param name Shader名称
* @return
*/
std::string ShaderCache::getCachePath(const std::string& name) const {
return cacheDir_ + "/" + name + ".cache";
}
/**
* @brief
* @return truefalse
*/
bool ShaderCache::ensureCacheDirectory() {
if (cacheDir_.empty()) {
return false;
}
std::error_code ec;
if (!fs::exists(cacheDir_)) {
if (!fs::create_directories(cacheDir_, ec)) {
return false;
}
}
return true;
}
} // namespace extra2d

View File

@ -0,0 +1,164 @@
#include <extra2d/graphics/shader/shader_hot_reloader.h>
#include <extra2d/utils/logger.h>
#include <chrono>
#include <filesystem>
namespace extra2d {
namespace fs = std::filesystem;
/**
* @brief
* @return
*/
ShaderHotReloader& ShaderHotReloader::getInstance() {
static ShaderHotReloader instance;
return instance;
}
/**
* @brief
* @return truefalse
*/
bool ShaderHotReloader::init() {
if (initialized_) {
return true;
}
#ifdef _WIN32
buffer_.resize(4096);
#endif
initialized_ = true;
E2D_LOG_INFO("Shader hot reloader initialized");
return true;
}
/**
* @brief
*/
void ShaderHotReloader::shutdown() {
if (!initialized_) {
return;
}
#ifdef _WIN32
if (watchHandle_ != nullptr) {
FindCloseChangeNotification(watchHandle_);
watchHandle_ = nullptr;
}
#endif
watchMap_.clear();
initialized_ = false;
enabled_ = false;
E2D_LOG_INFO("Shader hot reloader shutdown");
}
/**
* @brief Shader文件监视
* @param shaderName Shader名称
* @param filePaths
* @param callback
*/
void ShaderHotReloader::watch(const std::string& shaderName,
const std::vector<std::string>& filePaths,
FileChangeCallback callback) {
if (!initialized_) {
E2D_LOG_WARN("Hot reloader not initialized");
return;
}
WatchInfo info;
info.filePaths = filePaths;
info.callback = callback;
for (const auto& path : filePaths) {
info.modifiedTimes[path] = getFileModifiedTime(path);
}
watchMap_[shaderName] = std::move(info);
E2D_LOG_DEBUG("Watching shader: {} ({} files)", shaderName, filePaths.size());
}
/**
* @brief
* @param shaderName Shader名称
*/
void ShaderHotReloader::unwatch(const std::string& shaderName) {
auto it = watchMap_.find(shaderName);
if (it != watchMap_.end()) {
watchMap_.erase(it);
E2D_LOG_DEBUG("Stopped watching shader: {}", shaderName);
}
}
/**
* @brief
*/
void ShaderHotReloader::update() {
if (!initialized_ || !enabled_) {
return;
}
pollChanges();
}
/**
* @brief /
* @param enabled
*/
void ShaderHotReloader::setEnabled(bool enabled) {
enabled_ = enabled;
E2D_LOG_DEBUG("Hot reload {}", enabled ? "enabled" : "disabled");
}
/**
* @brief
*/
void ShaderHotReloader::pollChanges() {
auto now = static_cast<uint64_t>(
std::chrono::system_clock::now().time_since_epoch().count());
for (auto& pair : watchMap_) {
WatchInfo& info = pair.second;
for (const auto& filePath : info.filePaths) {
uint64_t currentModTime = getFileModifiedTime(filePath);
uint64_t lastModTime = info.modifiedTimes[filePath];
if (currentModTime != 0 && lastModTime != 0 && currentModTime != lastModTime) {
info.modifiedTimes[filePath] = currentModTime;
FileChangeEvent event;
event.filepath = filePath;
event.type = FileChangeEvent::Type::Modified;
event.timestamp = now;
E2D_LOG_DEBUG("Shader file changed: {}", filePath);
if (info.callback) {
info.callback(event);
}
}
}
}
}
/**
* @brief
* @param filepath
* @return
*/
uint64_t ShaderHotReloader::getFileModifiedTime(const std::string& filepath) {
try {
auto ftime = fs::last_write_time(filepath);
auto sctp = std::chrono::time_point_cast<std::chrono::seconds>(
ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now());
return static_cast<uint64_t>(sctp.time_since_epoch().count());
} catch (...) {
return 0;
}
}
} // namespace extra2d

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>
#include <chrono> #include <chrono>
#include <filesystem> #include <filesystem>

View File

@ -0,0 +1,184 @@
#include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/graphics/shader/shader_preset.h>
#include <extra2d/utils/logger.h>
namespace extra2d {
/**
* @brief
* @param params
* @return
*/
Ptr<IShader> ShaderPreset::Water(const WaterParams &params) {
Ptr<IShader> shader = ShaderManager::getInstance().get("water");
if (!shader) {
E2D_LOG_ERROR("Failed to get water shader");
return nullptr;
}
shader->setFloat("u_waveSpeed", params.waveSpeed);
shader->setFloat("u_waveAmplitude", params.waveAmplitude);
shader->setFloat("u_waveFrequency", params.waveFrequency);
return shader;
}
/**
* @brief
* @param params
* @return
*/
Ptr<IShader> ShaderPreset::Outline(const OutlineParams &params) {
Ptr<IShader> shader = ShaderManager::getInstance().get("outline");
if (!shader) {
E2D_LOG_ERROR("Failed to get outline shader");
return nullptr;
}
shader->setVec4("u_outlineColor", glm::vec4(params.color.r, params.color.g,
params.color.b, params.color.a));
shader->setFloat("u_thickness", params.thickness);
return shader;
}
/**
* @brief
* @param params
* @return
*/
Ptr<IShader> ShaderPreset::Distortion(const DistortionParams &params) {
Ptr<IShader> shader = ShaderManager::getInstance().get("distortion");
if (!shader) {
E2D_LOG_ERROR("Failed to get distortion shader");
return nullptr;
}
shader->setFloat("u_distortionAmount", params.distortionAmount);
shader->setFloat("u_timeScale", params.timeScale);
return shader;
}
/**
* @brief
* @param params
* @return
*/
Ptr<IShader> ShaderPreset::Pixelate(const PixelateParams &params) {
Ptr<IShader> shader = ShaderManager::getInstance().get("pixelate");
if (!shader) {
E2D_LOG_ERROR("Failed to get pixelate shader");
return nullptr;
}
shader->setFloat("u_pixelSize", params.pixelSize);
return shader;
}
/**
* @brief
* @param params
* @return
*/
Ptr<IShader> ShaderPreset::Invert(const InvertParams &params) {
Ptr<IShader> shader = ShaderManager::getInstance().get("invert");
if (!shader) {
E2D_LOG_ERROR("Failed to get invert shader");
return nullptr;
}
shader->setFloat("u_strength", params.strength);
return shader;
}
/**
* @brief
* @param params
* @return
*/
Ptr<IShader> ShaderPreset::Grayscale(const GrayscaleParams &params) {
Ptr<IShader> shader = ShaderManager::getInstance().get("grayscale");
if (!shader) {
E2D_LOG_ERROR("Failed to get grayscale shader");
return nullptr;
}
shader->setFloat("u_intensity", params.intensity);
return shader;
}
/**
* @brief
* @param params
* @return
*/
Ptr<IShader> ShaderPreset::Blur(const BlurParams &params) {
Ptr<IShader> shader = ShaderManager::getInstance().get("blur");
if (!shader) {
E2D_LOG_ERROR("Failed to get blur shader");
return nullptr;
}
shader->setFloat("u_radius", params.radius);
return shader;
}
/**
* @brief +
* @param grayParams
* @param outlineParams
* @return
*/
Ptr<IShader>
ShaderPreset::GrayscaleOutline(const GrayscaleParams &grayParams,
const OutlineParams &outlineParams) {
std::string shaderDir = ShaderManager::getInstance().getShaderDir();
std::string shaderPath = shaderDir + "effects/grayscale_outline.shader";
Ptr<IShader> shader =
ShaderManager::getInstance().loadFromCombinedFile(shaderPath);
if (!shader) {
E2D_LOG_ERROR("Failed to load grayscale_outline shader from: {}",
shaderPath);
return nullptr;
}
shader->setFloat("u_grayIntensity", grayParams.intensity);
shader->setVec4("u_outlineColor",
glm::vec4(outlineParams.color.r, outlineParams.color.g,
outlineParams.color.b, outlineParams.color.a));
shader->setFloat("u_thickness", outlineParams.thickness);
return shader;
}
/**
* @brief +
* @param pixParams
* @param invParams
* @return
*/
Ptr<IShader> ShaderPreset::PixelateInvert(const PixelateParams &pixParams,
const InvertParams &invParams) {
std::string shaderDir = ShaderManager::getInstance().getShaderDir();
std::string shaderPath = shaderDir + "effects/pixelate_invert.shader";
Ptr<IShader> shader =
ShaderManager::getInstance().loadFromCombinedFile(shaderPath);
if (!shader) {
E2D_LOG_ERROR("Failed to load pixelate_invert shader from: {}", shaderPath);
return nullptr;
}
shader->setFloat("u_pixelSize", pixParams.pixelSize);
shader->setFloat("u_invertStrength", invParams.strength);
return shader;
}
} // 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,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/services/event_service.h> #include <extra2d/services/event_service.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 evt =
@ -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 evt =

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

View File

@ -1,6 +1,6 @@
#include <extra2d/scene/transition_slide_scene.h> #include <extra2d/scene/transition_slide_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 {
@ -35,6 +35,14 @@ Ptr<TransitionSlideScene> TransitionSlideScene::create(
void TransitionSlideScene::onTransitionStart() { void TransitionSlideScene::onTransitionStart() {
} }
/**
* @brief
* @param dt
*/
void TransitionSlideScene::updateTransition(float dt) {
TransitionScene::updateTransition(dt);
}
/** /**
* @brief * @brief
* @param renderer * @param renderer
@ -59,9 +67,6 @@ void TransitionSlideScene::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_;
@ -131,9 +136,6 @@ void TransitionSlideScene::renderContent(RenderBackend &renderer) {
} }
} }
if (progress_ >= 1.0f && !isFinished_) {
finish();
}
} }
} // namespace extra2d } // namespace extra2d

View File

@ -0,0 +1,171 @@
#include <extra2d/services/logger_service.h>
#include <extra2d/core/service_locator.h>
#include <cstdio>
#include <cstdarg>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <mutex>
namespace extra2d {
// ConsoleLogger 实现
class ConsoleLogger::Impl {
public:
std::mutex mutex_;
};
ConsoleLogger::ConsoleLogger() : level_(LogLevel::Info), impl_(std::make_unique<Impl>()) {
info_.name = "ConsoleLogger";
info_.priority = ServicePriority::Core;
}
ConsoleLogger::~ConsoleLogger() = default;
bool ConsoleLogger::initialize() {
setState(ServiceState::Running);
return true;
}
void ConsoleLogger::shutdown() {
setState(ServiceState::Stopped);
}
void ConsoleLogger::setLevel(LogLevel level) {
level_ = level;
}
LogLevel ConsoleLogger::getLevel() const {
return level_;
}
bool ConsoleLogger::isEnabled(LogLevel level) const {
return static_cast<int>(level) >= static_cast<int>(level_);
}
void ConsoleLogger::log(LogLevel level, const char* fmt, ...) {
if (!isEnabled(level)) return;
char buffer[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
output(level, buffer);
}
void ConsoleLogger::log(LogLevel level, const std::string& msg) {
if (!isEnabled(level)) return;
output(level, msg.c_str());
}
void ConsoleLogger::trace(const char* fmt, ...) {
if (!isEnabled(LogLevel::Trace)) return;
char buffer[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
output(LogLevel::Trace, buffer);
}
void ConsoleLogger::debug(const char* fmt, ...) {
if (!isEnabled(LogLevel::Debug)) return;
char buffer[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
output(LogLevel::Debug, buffer);
}
void ConsoleLogger::info(const char* fmt, ...) {
if (!isEnabled(LogLevel::Info)) return;
char buffer[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
output(LogLevel::Info, buffer);
}
void ConsoleLogger::warn(const char* fmt, ...) {
if (!isEnabled(LogLevel::Warn)) return;
char buffer[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
output(LogLevel::Warn, buffer);
}
void ConsoleLogger::error(const char* fmt, ...) {
if (!isEnabled(LogLevel::Error)) return;
char buffer[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
output(LogLevel::Error, buffer);
}
void ConsoleLogger::fatal(const char* fmt, ...) {
if (!isEnabled(LogLevel::Fatal)) return;
char buffer[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
output(LogLevel::Fatal, buffer);
}
void ConsoleLogger::output(LogLevel level, const char* msg) {
std::lock_guard<std::mutex> lock(impl_->mutex_);
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()) % 1000;
std::tm tm;
#ifdef _WIN32
localtime_s(&tm, &time);
#else
localtime_r(&time, &tm);
#endif
const char* levelStr = getLevelString(level);
// 颜色代码
const char* color = "";
const char* reset = "\033[0m";
switch (level) {
case LogLevel::Trace: color = "\033[90m"; break;
case LogLevel::Debug: color = "\033[36m"; break;
case LogLevel::Info: color = "\033[32m"; break;
case LogLevel::Warn: color = "\033[33m"; break;
case LogLevel::Error: color = "\033[31m"; break;
case LogLevel::Fatal: color = "\033[35m"; break;
default: break;
}
printf("%s[%02d:%02d:%02d.%03d] [%s] %s%s\n",
color, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)ms.count(),
levelStr, msg, reset);
}
const char* ConsoleLogger::getLevelString(LogLevel level) {
switch (level) {
case LogLevel::Trace: return "TRACE";
case LogLevel::Debug: return "DEBUG";
case LogLevel::Info: return "INFO";
case LogLevel::Warn: return "WARN";
case LogLevel::Error: return "ERROR";
case LogLevel::Fatal: return "FATAL";
default: return "UNKNOWN";
}
}
} // namespace extra2d

View File

@ -1,177 +0,0 @@
#include <extra2d/utils/logger.h>
#include <cstdio>
#include <ctime>
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef __SWITCH__
#include <switch.h>
#endif
namespace extra2d {
LogLevel Logger::level_ = LogLevel::Info;
bool Logger::initialized_ = false;
bool Logger::consoleOutput_ = true;
bool Logger::fileOutput_ = false;
std::string Logger::logFile_;
void *Logger::logFileHandle_ = nullptr;
const char *Logger::getLevelString(LogLevel level) {
switch (level) {
case LogLevel::Trace:
return "TRACE";
case LogLevel::Debug:
return "DEBUG";
case LogLevel::Info:
return "INFO ";
case LogLevel::Warn:
return "WARN ";
case LogLevel::Error:
return "ERROR";
case LogLevel::Fatal:
return "FATAL";
default:
return "UNKNOWN";
}
}
void Logger::writeToConsole(LogLevel level, const char *msg) {
const char *levelStr = getLevelString(level);
#ifdef _WIN32
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
WORD color = 7;
switch (level) {
case LogLevel::Trace: color = 8; break;
case LogLevel::Debug: color = 8; break;
case LogLevel::Info: color = 7; break;
case LogLevel::Warn: color = 14; break;
case LogLevel::Error: color = 12; break;
case LogLevel::Fatal: color = 12 | FOREGROUND_INTENSITY; break;
default: break;
}
SetConsoleTextAttribute(hConsole, color);
printf("[%s] %s\n", levelStr, msg);
SetConsoleTextAttribute(hConsole, 7);
#else
const char *colorCode = "\033[0m";
switch (level) {
case LogLevel::Trace: colorCode = "\033[90m"; break;
case LogLevel::Debug: colorCode = "\033[90m"; break;
case LogLevel::Info: colorCode = "\033[0m"; break;
case LogLevel::Warn: colorCode = "\033[33m"; break;
case LogLevel::Error: colorCode = "\033[31m"; break;
case LogLevel::Fatal: colorCode = "\033[1;31m"; break;
default: break;
}
printf("%s[%s] %s\033[0m\n", colorCode, levelStr, msg);
#endif
}
void Logger::writeToFile(LogLevel level, const char *msg) {
if (!logFileHandle_) return;
FILE *fp = static_cast<FILE *>(logFileHandle_);
time_t now = time(nullptr);
struct tm *tm_info = localtime(&now);
char timeBuf[32];
strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", tm_info);
fprintf(fp, "[%s] [%s] %s\n", timeBuf, getLevelString(level), msg);
fflush(fp);
}
void Logger::outputLog(LogLevel level, const char *msg) {
if (consoleOutput_) {
writeToConsole(level, msg);
}
if (fileOutput_) {
writeToFile(level, msg);
}
}
void Logger::init() {
if (initialized_) {
return;
}
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut != INVALID_HANDLE_VALUE) {
DWORD mode = 0;
if (GetConsoleMode(hOut, &mode)) {
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, mode);
}
}
#endif
#ifdef __SWITCH__
consoleInit(NULL);
#endif
initialized_ = true;
log(LogLevel::Info, "Logger initialized");
}
void Logger::shutdown() {
if (initialized_) {
log(LogLevel::Info, "Logger shutting down");
}
if (logFileHandle_) {
fclose(static_cast<FILE *>(logFileHandle_));
logFileHandle_ = nullptr;
}
#ifdef __SWITCH__
consoleExit(NULL);
#endif
initialized_ = false;
fileOutput_ = false;
}
void Logger::setLevel(LogLevel level) {
level_ = level;
}
void Logger::setConsoleOutput(bool enable) {
consoleOutput_ = enable;
}
void Logger::setFileOutput(const std::string &filename) {
if (logFileHandle_) {
fclose(static_cast<FILE *>(logFileHandle_));
logFileHandle_ = nullptr;
}
logFile_ = filename;
fileOutput_ = !filename.empty();
if (fileOutput_) {
#ifdef _WIN32
FILE *fp = nullptr;
fopen_s(&fp, filename.c_str(), "a");
#else
FILE *fp = fopen(filename.c_str(), "a");
#endif
logFileHandle_ = fp;
if (fp) {
time_t now = time(nullptr);
struct tm *tm_info = localtime(&now);
char timeBuf[32];
strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", tm_info);
fprintf(fp, "\n=== Log session started at %s ===\n", timeBuf);
fflush(fp);
}
}
}
} // namespace extra2d

186
README.md
View File

@ -25,7 +25,7 @@
<i>高性能、模块化、支持 Nintendo Switch</i> <i>高性能、模块化、支持 Nintendo Switch</i>
</p> </p>
[构建指南](#构建指南) | [快速开始](./docs/quick_start.md) | [示例程序](#示例程序) | [模块系统](./docs/module_system.md) [构建指南](#构建指南) | [快速开始](#快速开始) | [示例程序](#示例程序) | [模块系统](./docs/module_system.md)
</div> </div>
@ -39,11 +39,12 @@
- **跨平台支持**Windows、Linux、macOS、Nintendo Switch - **跨平台支持**Windows、Linux、macOS、Nintendo Switch
- **模块化架构**:模块系统 + 服务系统,灵活可扩展 - **模块化架构**:模块系统 + 服务系统,灵活可扩展
- **显式注册**:通过 `Application::use()` 注册模块,参考 Kiwano 设计
- **场景图系统**:树形节点结构,支持变换继承 - **场景图系统**:树形节点结构,支持变换继承
- **输入系统**:键盘、鼠标、手柄、触摸,事件驱动 - **输入系统**:键盘、鼠标、手柄、触摸,事件驱动
- **渲染系统**OpenGL ES 3.2,支持自定义着色器 - **渲染系统**OpenGL ES 3.2,支持自定义着色器
- **视口适配**:多种适配模式,自动响应窗口大小变化 - **视口适配**:多种适配模式,自动响应窗口大小变化
- **音频系统**:高质量音频播放(规划中)
- **UI 系统**:完整的 UI 控件支持(规划中)
--- ---
@ -58,19 +59,18 @@ flowchart TB
subgraph Core["Core Layer (核心层)"] subgraph Core["Core Layer (核心层)"]
direction LR direction LR
MOD[Module<br/>模块基类] MR[ModuleRegistry<br/>模块注册表]
SL[ServiceLocator<br/>服务定位器] SL[ServiceLocator<br/>服务定位器]
end end
subgraph Modules["Modules (模块系统)"] subgraph Modules["Modules (模块系统)"]
direction TB direction TB
subgraph CoreModules["Core Modules"] subgraph CoreModules["Core Modules"]
LOGGER[Logger Module<br/>日志系统]
CONFIG[Config Module<br/>配置管理] CONFIG[Config Module<br/>配置管理]
PLATFORM[Platform Module<br/>平台检测] PLATFORM[Platform Module<br/>平台检测]
WINDOW[Window Module<br/>窗口管理] WINDOW[Window Module<br/>窗口管理]
end end
subgraph FeatureModules["Feature Modules"] subgraph OtherModules["Feature Modules"]
INPUT[Input Module<br/>输入处理] INPUT[Input Module<br/>输入处理]
RENDER[Render Module<br/>渲染系统] RENDER[Render Module<br/>渲染系统]
end end
@ -106,9 +106,9 @@ flowchart TB
INPUT_SYS[Input System<br/>输入系统] INPUT_SYS[Input System<br/>输入系统]
end end
APP --> MOD APP --> MR
APP --> SL APP --> SL
MOD --> Modules MR --> Modules
SL --> Services SL --> Services
SCENE_SVC --> SCENE SCENE_SVC --> SCENE
SCENE --> NODE SCENE --> NODE
@ -127,36 +127,11 @@ flowchart TB
| 模块 | 职责 | 优先级 | | 模块 | 职责 | 优先级 |
|-----|------|-------| |-----|------|-------|
| Logger | 日志系统 | -1 | | Config | 配置管理 | Core (0) |
| Config | 配置管理 | 0 | | Platform | 平台检测 | Core (0) |
| Platform | 平台检测 | 10 | | Window | 窗口管理 | Core (0) |
| Window | 窗口管理 | 20 | | Input | 输入处理 | Input (50) |
| Input | 输入处理 | 30 | | Render | 渲染系统 | Graphics (100) |
| Render | 渲染系统 | 40 |
### 模块自发现原理
```mermaid
sequenceDiagram
participant C as 编译时
participant R as 运行时
participant MR as ModuleRegistry
participant App as Application
Note over C: E2D_MODULE 宏展开
C->>C: 创建静态变量 ModuleAutoRegister<T>
C->>C: 添加 __attribute__((used)) 防止优化
Note over R: 程序启动
R->>MR: 静态初始化阶段
MR->>MR: 构造函数自动调用 registerModule()
MR->>MR: 模块信息存入注册表
Note over App: Application::init()
App->>MR: getModules()
MR->>App: 返回按优先级排序的模块列表
App->>App: 按顺序调用 setupModule()
```
### 服务系统 ### 服务系统
@ -238,10 +213,8 @@ int main() {
config.appName = "My Game"; config.appName = "My Game";
config.appVersion = "1.0.0"; config.appVersion = "1.0.0";
// 获取应用实例
Application& app = Application::get();
// 初始化 // 初始化
Application& app = Application::get();
if (!app.init(config)) { if (!app.init(config)) {
return -1; return -1;
} }
@ -271,46 +244,6 @@ int main() {
} }
``` ```
### 创建自定义模块
```cpp
#include <extra2d/core/module.h>
class MyModule : public extra2d::Module {
public:
const char* getName() const override { return "MyModule"; }
int getPriority() const override { return 1000; }
void setupModule() override {
// 初始化资源
extra2d::E2D_LOG_INFO("MyModule initialized");
}
void destroyModule() override {
// 清理资源
extra2d::E2D_LOG_INFO("MyModule destroyed");
}
void onUpdate(extra2d::UpdateContext& ctx) override {
// 更新逻辑
ctx.next(); // 继续下一个模块
}
};
// 在 main 中注册
int main() {
auto& app = extra2d::Application::get();
MyModule myModule;
app.use(myModule); // 显式注册
app.init();
app.run();
return 0;
}
```
### 场景图示例 ### 场景图示例
```cpp ```cpp
@ -340,22 +273,22 @@ root->addChild(child);
auto eventService = app.events(); auto eventService = app.events();
// 键盘事件 // 键盘事件
eventService->addListener(EventType::KeyPress, [](Event& e) { eventService->addListener(EventType::KeyPressed, [](Event& e) {
auto& key = std::get<KeyEvent>(e.data); auto& key = std::get<KeyEvent>(e.data);
if (key.scancode == static_cast<int>(Key::Escape)) { if (key.keyCode == static_cast<int>(Key::Escape)) {
Application::get().quit(); Application::get().quit();
} }
}); });
// 鼠标事件 // 鼠标事件
eventService->addListener(EventType::MousePress, [](Event& e) { eventService->addListener(EventType::MouseButtonPressed, [](Event& e) {
auto& mouse = std::get<MouseEvent>(e.data); auto& mouse = std::get<MouseButtonEvent>(e.data);
// 处理鼠标点击 // 处理鼠标点击
}); });
// 手柄事件 // 手柄事件
eventService->addListener(EventType::GamepadPress, [](Event& e) { eventService->addListener(EventType::GamepadButtonPressed, [](Event& e) {
auto& gamepad = std::get<GamepadEvent>(e.data); auto& gamepad = std::get<GamepadButtonEvent>(e.data);
// 处理手柄输入 // 处理手柄输入
}); });
``` ```
@ -367,13 +300,11 @@ eventService->addListener(EventType::GamepadPress, [](Event& e) {
| 示例 | 说明 | | 示例 | 说明 |
|-----|------| |-----|------|
| `demo_basic` | 基础示例:场景图、输入事件、视口适配 | | `demo_basic` | 基础示例:场景图、输入事件、视口适配 |
| `demo_hello_module` | 自定义模块示例:展示如何创建和注册模块 |
运行示例: 运行示例:
```bash ```bash
xmake run demo_basic xmake run demo_basic
xmake run demo_hello_module
``` ```
--- ---
@ -395,7 +326,6 @@ xmake run demo_hello_module
## 文档 ## 文档
- [快速入门指南](./docs/quick_start.md) - 从安装到创建第一个游戏
- [模块系统文档](./docs/module_system.md) - 模块系统、服务系统、场景图、视口适配 - [模块系统文档](./docs/module_system.md) - 模块系统、服务系统、场景图、视口适配
--- ---
@ -409,36 +339,98 @@ Extra2D/
│ │ ├── KHR/ # KHR 平台头文件 │ │ ├── KHR/ # KHR 平台头文件
│ │ ├── extra2d/ # 引擎公共头文件 │ │ ├── extra2d/ # 引擎公共头文件
│ │ │ ├── app/ # 应用程序 │ │ │ ├── app/ # 应用程序
│ │ │ ├── audio/ # 音频配置
│ │ │ ├── config/ # 配置系统 │ │ │ ├── config/ # 配置系统
│ │ │ ├── core/ # 核心类型、模块基类 │ │ │ ├── core/ # 核心类型
│ │ │ ├── event/ # 事件系统 │ │ │ ├── event/ # 事件系统
│ │ │ ├── graphics/ # 图形渲染 │ │ │ ├── graphics/ # 图形渲染
│ │ │ ├── input/ # 输入配置 │ │ │ │ ├── core/ # 渲染核心
│ │ │ ├── modules/ # 内置模块 │ │ │ │ ├── camera/ # 相机和视口
│ │ │ │ ├── shader/ # Shader 系统
│ │ │ │ ├── texture/ # 纹理系统
│ │ │ │ ├── memory/ # GPU 内存管理
│ │ │ │ └── opengl/ # OpenGL 实现
│ │ │ ├── platform/ # 平台抽象 │ │ │ ├── platform/ # 平台抽象
│ │ │ ├── scene/ # 场景系统 │ │ │ ├── scene/ # 场景系统
│ │ │ ├── script/ # 脚本系统 │ │ │ ├── services/ # 服务接口
│ │ │ └── utils/ # 工具库
│ │ ├── glad/ # OpenGL 加载器
│ │ └── stb/ # STB 单文件库
│ ├── shaders/ # 着色器文件
│ │ ├── builtin/ # 内置着色器
│ │ ├── common/ # 公共着色器代码
│ │ └── effects/ # 特效着色器
│ └── src/ # 源文件
│ ├── app/ # 应用实现
│ ├── config/ # 配置实现
│ ├── core/ # 核心实现
│ ├── event/ # 事件实现
│ ├── glad/ # GLAD 实现
│ ├── graphics/ # 图形实现
│ │ ├── core/ # 渲染核心
│ │ ├── camera/ # 相机和视口
│ │ ├── shader/ # Shader 系统
│ │ ├── texture/ # 纹理系统
│ │ ├── memory/ # GPU 内存管理
│ │ └── opengl/ # OpenGL 实现
│ ├── platform/ # 平台实现
│ │ └── backends/ # 后端实现
│ │ └── sdl2/ # SDL2 后端
│ ├── scene/ # 场景实现
│ ├── services/ # 服务实现
│ └── utils/ # 工具实现
├── docs/ # 文档
├── examples/ # 示例程序
│ └── basic/ # 基础示例
└── xmake/ # 构建配置
└── toolchains/ # 工具链配置
```
Extra2D/
├── Extra2D/
│ ├── include/
│ │ ├── KHR/ # KHR 平台头文件
│ │ ├── extra2d/ # 引擎公共头文件
│ │ │ ├── app/ # 应用程序
│ │ │ ├── audio/ # 音频配置
│ │ │ ├── config/ # 配置系统
│ │ │ ├── core/ # 核心类型
│ │ │ ├── debug/ # 调试配置
│ │ │ ├── event/ # 事件系统
│ │ │ ├── graphics/ # 图形渲染
│ │ │ │ └── opengl/ # OpenGL 实现
│ │ │ ├── input/ # 输入配置
│ │ │ ├── platform/ # 平台抽象
│ │ │ ├── resource/ # 资源配置
│ │ │ ├── scene/ # 场景系统
│ │ │ ├── services/ # 服务接口 │ │ │ ├── services/ # 服务接口
│ │ │ └── utils/ # 工具库 │ │ │ └── utils/ # 工具库
│ │ ├── glad/ # OpenGL 加载器 │ │ ├── glad/ # OpenGL 加载器
│ │ └── stb/ # STB 单文件库 │ │ └── stb/ # STB 单文件库
│ ├── shaders/ # 着色器文件 │ ├── shaders/ # 着色器文件
│ │ ├── builtin/ # 内置着色器
│ │ ├── common/ # 公共着色器代码
│ │ └── effects/ # 特效着色器
│ └── src/ # 源文件 │ └── src/ # 源文件
│ ├── app/ # 应用实现 │ ├── app/ # 应用实现
│ ├── config/ # 配置实现
│ ├── core/ # 核心实现 │ ├── core/ # 核心实现
│ ├── debug/ # 调试实现
│ ├── event/ # 事件实现 │ ├── event/ # 事件实现
│ ├── glad/ # GLAD 实现
│ ├── graphics/ # 图形实现 │ ├── graphics/ # 图形实现
│ ├── modules/ # 模块实现 │ └── opengl/ # OpenGL 实现
│ ├── platform/ # 平台实现 │ ├── platform/ # 平台实现
│ │ └── backends/ # 后端实现
│ │ └── sdl2/ # SDL2 后端
│ ├── resource/ # 资源实现
│ ├── scene/ # 场景实现 │ ├── scene/ # 场景实现
│ ├── script/ # 脚本实现
│ ├── services/ # 服务实现 │ ├── services/ # 服务实现
│ └── utils/ # 工具实现 │ └── utils/ # 工具实现
├── docs/ # 文档 ├── docs/ # 文档
├── examples/ # 示例程序 ├── examples/ # 示例程序
│ ├── basic/ # 基础示例 │ └── basic/ # 基础示例
│ └── hello_module/ # 自定义模块示例
└── xmake/ # 构建配置 └── xmake/ # 构建配置
└── toolchains/ # 工具链配置
``` ```
--- ---

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +1,19 @@
/** /**
* @file main.cpp * @file main.cpp
* @brief Extra2D * @brief Extra2D
*
*
* -
* -
* -
* -
*
* E2D_MODULE app.use()
*/ */
#include <extra2d/extra2d.h> #include <extra2d/extra2d.h>
#include <extra2d/platform/window_module.h>
#include <extra2d/platform/input_module.h>
#include <extra2d/graphics/core/render_module.h>
#include <extra2d/core/service_locator.h>
#include <extra2d/services/event_service.h>
#include <extra2d/services/camera_service.h>
#include <iostream> #include <iostream>
using namespace extra2d; using namespace extra2d;
/**
* @brief
*/
void createSceneGraph(Scene *scene) { void createSceneGraph(Scene *scene) {
float width = scene->getWidth(); float width = scene->getWidth();
float height = scene->getHeight(); float height = scene->getHeight();
@ -87,7 +82,7 @@ void createSceneGraph(Scene *scene) {
std::cout << " └── Root (center)" << std::endl; std::cout << " └── Root (center)" << std::endl;
std::cout << " ├── Parent1 (left)" << std::endl; std::cout << " ├── Parent1 (left)" << std::endl;
std::cout << " │ ├── RedRect (100x100)" << std::endl; std::cout << " │ ├── RedRect (100x100)" << std::endl;
std::cout << " │ └── Child1 (rotated 45°, scaled 0.5)" << std::endl; std::cout << " │ └── Child1 (rotated 45, scaled 0.5)" << std::endl;
std::cout << " │ └── OrangeRect (60x60)" << std::endl; std::cout << " │ └── OrangeRect (60x60)" << std::endl;
std::cout << " ├── Parent2 (right)" << std::endl; std::cout << " ├── Parent2 (right)" << std::endl;
std::cout << " │ ├── BlueCircle (radius 60)" << std::endl; std::cout << " │ ├── BlueCircle (radius 60)" << std::endl;
@ -98,45 +93,54 @@ void createSceneGraph(Scene *scene) {
std::cout << "=============================\n" << std::endl; std::cout << "=============================\n" << std::endl;
} }
/**
* @brief
*/
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
(void)argc; (void)argc;
(void)argv; (void)argv;
std::cout << "Extra2D Scene Graph Demo - Starting..." << std::endl; std::cout << "Extra2D Scene Graph Demo - Starting..." << std::endl;
AppConfig config = AppConfig::createDefault();
config.appName = "Extra2D Scene Graph Demo";
config.appVersion = "1.0.0";
Application &app = Application::get(); Application &app = Application::get();
// 模块已通过 E2D_MODULE 宏自动注册,无需调用 app.use() // 注册模块(按优先级顺序)
WindowModule::Cfg winCfg;
winCfg.w = 1280;
winCfg.h = 720;
winCfg.priority = 0;
app.use<WindowModule>(winCfg);
if (!app.init(config)) { RenderModule::Cfg renderCfg;
renderCfg.priority = 10;
app.use<RenderModule>(renderCfg);
InputModule::Cfg inputCfg;
inputCfg.priority = 20;
app.use<InputModule>(inputCfg);
std::cout << "Initializing application..." << std::endl;
if (!app.init()) {
std::cerr << "Failed to initialize application!" << std::endl; std::cerr << "Failed to initialize application!" << std::endl;
return -1; return -1;
} }
std::cout << "Application initialized successfully!" << std::endl; std::cout << "Application initialized successfully!" << std::endl;
std::cout << "Window: " << app.window().width() << "x"
<< app.window().height() << std::endl; auto* win = app.window();
if (win) {
std::cout << "Window: " << win->width() << "x" << win->height() << std::endl;
}
auto eventService = app.events(); auto eventService = ServiceLocator::instance().getService<IEventService>();
if (eventService) { if (eventService) {
eventService->addListener(EventType::KeyPress, [](Event &e) { eventService->addListener(EventType::KeyPressed, [](Event &e) {
auto &keyEvent = std::get<KeyEvent>(e.data); auto &keyEvent = std::get<KeyEvent>(e.data);
if (keyEvent.keyCode == static_cast<int>(Key::Escape)) {
if (keyEvent.scancode == static_cast<int>(Key::Escape)) {
e.handled = true; e.handled = true;
Application::get().quit(); Application::get().quit();
} }
}); });
eventService->addListener(EventType::MousePress, [](Event &e) { eventService->addListener(EventType::MouseButtonPressed, [](Event &e) {
auto &mouseEvent = std::get<MouseEvent>(e.data); auto &mouseEvent = std::get<MouseButtonEvent>(e.data);
std::cout << "[Click] Button " << mouseEvent.button << " at (" std::cout << "[Click] Button " << mouseEvent.button << " at ("
<< mouseEvent.position.x << ", " << mouseEvent.position.y << ")" << mouseEvent.position.x << ", " << mouseEvent.position.y << ")"
<< std::endl; << std::endl;
@ -145,22 +149,23 @@ int main(int argc, char *argv[]) {
auto scene = Scene::create(); auto scene = Scene::create();
scene->setBackgroundColor(Color(0.12f, 0.12f, 0.16f, 1.0f)); scene->setBackgroundColor(Color(0.12f, 0.12f, 0.16f, 1.0f));
scene->setViewportSize(static_cast<float>(app.window().width()), if (win) {
static_cast<float>(app.window().height())); scene->setViewportSize(static_cast<float>(win->width()),
static_cast<float>(win->height()));
}
auto cameraService = app.camera(); auto cameraService = ServiceLocator::instance().getService<ICameraService>();
if (cameraService) { if (cameraService && win) {
ViewportConfig vpConfig; ViewportConfig vpConfig;
vpConfig.logicWidth = static_cast<float>(app.window().width()); vpConfig.logicWidth = static_cast<float>(win->width());
vpConfig.logicHeight = static_cast<float>(app.window().height()); vpConfig.logicHeight = static_cast<float>(win->height());
vpConfig.mode = ViewportMode::AspectRatio; vpConfig.mode = ViewportMode::AspectRatio;
cameraService->setViewportConfig(vpConfig); cameraService->setViewportConfig(vpConfig);
cameraService->updateViewport(app.window().width(), app.window().height()); cameraService->updateViewport(win->width(), win->height());
cameraService->applyViewportAdapter(); cameraService->applyViewportAdapter();
} }
createSceneGraph(scene.get()); createSceneGraph(scene.get());
app.enterScene(scene); app.enterScene(scene);
std::cout << "\nControls:" << std::endl; std::cout << "\nControls:" << std::endl;
@ -170,6 +175,9 @@ int main(int argc, char *argv[]) {
app.run(); app.run();
std::cout << "Shutting down..." << std::endl;
app.shutdown();
std::cout << "Goodbye!" << std::endl; std::cout << "Goodbye!" << std::endl;
return 0; return 0;
} }

View File

@ -1,82 +1,38 @@
#include "hello_module.h" #include "hello_module.h"
#include <extra2d/core/module_macros.h> #include <iostream>
#include <extra2d/utils/logger.h>
namespace extra2d { namespace extra2d {
HelloModule::HelloModule() HelloModule::HelloModule(const Cfg& cfg) : cfg_(cfg) {}
: Module()
, config_()
, time_(0.0f) {
}
HelloModule::~HelloModule() { HelloModule::~HelloModule() {
if (isInitialized()) { if (initialized_) {
destroyModule(); shutdown();
} }
} }
void HelloModule::setupModule() { bool HelloModule::init() {
if (isInitialized()) { if (initialized_) return true;
return;
}
if (config_.greeting.empty()) {
config_.greeting = "Hello, Extra2D!";
}
if (config_.repeatCount <= 0) {
config_.repeatCount = 1;
}
setInitialized(true);
E2D_LOG_INFO("HelloModule initialized"); std::cout << "HelloModule initialized" << std::endl;
E2D_LOG_INFO(" Greeting: {}", config_.greeting); std::cout << " Greeting: " << cfg_.greeting << std::endl;
E2D_LOG_INFO(" Repeat Count: {}", config_.repeatCount); std::cout << " Repeat count: " << cfg_.repeatCount << std::endl;
E2D_LOG_INFO(" Logging Enabled: {}", config_.enableLogging);
initialized_ = true;
sayHello(); return true;
} }
void HelloModule::destroyModule() { void HelloModule::shutdown() {
if (!isInitialized()) { if (!initialized_) return;
return;
} std::cout << "HelloModule shutdown" << std::endl;
initialized_ = false;
if (config_.enableLogging) {
E2D_LOG_INFO("HelloModule shutdown - Goodbye!");
}
setInitialized(false);
}
void HelloModule::onUpdate(UpdateContext& ctx) {
if (!isInitialized()) {
ctx.next();
return;
}
time_ += ctx.getDeltaTime();
if (time_ >= 5.0f) {
sayHello();
time_ = 0.0f;
}
ctx.next();
} }
void HelloModule::sayHello() const { void HelloModule::sayHello() const {
if (!config_.enableLogging) { for (int i = 0; i < cfg_.repeatCount; ++i) {
return; std::cout << cfg_.greeting << std::endl;
}
for (int i = 0; i < config_.repeatCount; ++i) {
E2D_LOG_INFO("[HelloModule] {}", config_.greeting);
} }
} }
} // namespace extra2d } // namespace extra2d
E2D_MODULE(HelloModule, 1000)

View File

@ -1,78 +1,50 @@
#pragma once #pragma once
#include <extra2d/core/export.h>
#include <extra2d/core/module.h> #include <extra2d/core/module.h>
#include <string> #include <string>
namespace extra2d { namespace extra2d {
/** /**
* @brief Hello模块配置数据结构 * @brief Hello模块示例
*/ *
struct HelloModuleConfigData {
std::string greeting = "Hello, Extra2D!";
int repeatCount = 1;
bool enableLogging = true;
};
/**
* @brief Hello模块
*
*
* 1. Module
* 2.
* 3. 使 E2D_MODULE
*/ */
class HelloModule : public Module { class HelloModule : public Module {
public: public:
/** /**
* @brief * @brief
*/ */
HelloModule(); struct Cfg {
std::string greeting = "Hello, Extra2D!";
/** int repeatCount = 1;
* @brief int priority = 100;
*/ };
~HelloModule() override;
/**
/** * @brief
* @brief * @param cfg
*/ */
const char *getName() const override { return "HelloModule"; } explicit HelloModule(const Cfg& cfg = Cfg{});
/** /**
* @brief * @brief
*/ */
int getPriority() const override { return 1000; } ~HelloModule() override;
/** bool init() override;
* @brief void shutdown() override;
*/ bool ok() const override { return initialized_; }
void setupModule() override; const char* name() const override { return "hello"; }
int priority() const override { return cfg_.priority; }
/**
* @brief /**
*/ * @brief
void destroyModule() override; */
void sayHello() const;
/**
* @brief
*/
void onUpdate(UpdateContext &ctx) override;
/**
* @brief
*/
void setConfig(const HelloModuleConfigData &config) { config_ = config; }
/**
* @brief
*/
void sayHello() const;
private: private:
HelloModuleConfigData config_; Cfg cfg_;
float time_ = 0.0f; bool initialized_ = false;
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -1,61 +1,82 @@
#include "hello_module.h" #include "hello_module.h"
#include <extra2d/extra2d.h> #include <extra2d/app/application.h>
#include <extra2d/platform/window_module.h>
#include <extra2d/graphics/core/render_module.h>
#include <extra2d/scene/scene.h>
#include <extra2d/services/scene_service.h>
#include <iostream>
using namespace extra2d; using namespace extra2d;
/**
* @brief
*
* 使
*/
class HelloScene : public Scene { class HelloScene : public Scene {
public: public:
static Ptr<HelloScene> create() { return makeShared<HelloScene>(); } static Ptr<HelloScene> create() { return makeShared<HelloScene>(); }
void onEnter() override { void onEnter() override {
Scene::onEnter(); Scene::onEnter();
addListener(EventType::KeyPress, [](Event &e) { std::cout << "HelloScene entered" << std::endl;
auto &keyEvent = std::get<KeyEvent>(e.data);
auto app = Application::get().getModule<HelloModule>();
E2D_LOG_INFO("Module {}", app->getName());
if (keyEvent.scancode == static_cast<int>(Key::Escape)) {
e.handled = true;
E2D_LOG_INFO("ESC pressed, exiting...");
Application::get().quit();
}
});
E2D_LOG_INFO("HelloScene entered");
setBackgroundColor(Color(0.1f, 0.1f, 0.2f, 1.0f)); setBackgroundColor(Color(0.1f, 0.1f, 0.2f, 1.0f));
auto* hello = Application::get().get<HelloModule>();
if (hello) {
std::cout << "Scene calling HelloModule from onEnter..." << std::endl;
hello->sayHello();
}
}
void onUpdate(float dt) override {
Scene::onUpdate(dt);
time_ += dt;
if (time_ >= 5.0f) {
auto* hello = Application::get().get<HelloModule>();
if (hello) {
std::cout << "Scene calling HelloModule from onUpdate..." << std::endl;
hello->sayHello();
}
time_ = 0.0f;
}
} }
private: private:
float time_ = 0.0f;
}; };
/**
* @brief
*
*
*/
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
(void)argc; (void)argc;
(void)argv; (void)argv;
std::cout << "=== Hello Module Example ===" << std::endl;
std::cout << "This example demonstrates how to create a custom module" << std::endl;
std::cout << "" << std::endl;
Application &app = Application::get(); Application &app = Application::get();
AppConfig appConfig; // 注册模块
appConfig.appName = "HelloModule Example"; app.use<WindowModule>(WindowModule::Cfg{.w = 800, .h = 600});
appConfig.appVersion = "1.0.0"; app.use<RenderModule>();
app.use<HelloModule>(HelloModule::Cfg{.greeting = "Hello from custom module!", .repeatCount = 3});
if (!app.init(appConfig)) { if (!app.init()) {
E2D_LOG_ERROR("Failed to initialize application"); std::cerr << "Failed to initialize application" << std::endl;
return 1; return 1;
} }
std::cout << "" << std::endl;
std::cout << "Application initialized successfully" << std::endl;
std::cout << "" << std::endl;
auto scene = HelloScene::create(); auto scene = HelloScene::create();
app.enterScene(scene); app.enterScene(scene);
std::cout << "Starting main loop..." << std::endl;
std::cout << "Press ESC or close window to exit" << std::endl;
std::cout << "" << std::endl;
app.run(); app.run();
std::cout << "Application shutting down..." << std::endl;
app.shutdown();
std::cout << "Application shutdown complete" << std::endl;
return 0; return 0;
} }