refactor(图形后端): 重构图形后端系统为基于工厂的注册机制
- 移除BackendType枚举,改为使用字符串标识后端类型 - 实现图形后端工厂类,支持动态注册和创建后端 - 添加自动兼容性检查功能,根据窗口后端选择兼容的渲染器 - 移除WindowConfigData结构,简化窗口创建接口 - 更新示例代码以使用新的后端系统 - 添加OpenGL和Vulkan后端自动注册实现 - 清理无用代码和过时接口
This commit is contained in:
parent
583e866861
commit
8e06bb0adb
|
|
@ -1,79 +1,124 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/core/smart_ptr.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace graphics {
|
||||
|
||||
/**
|
||||
* @brief 渲染后端类型枚举
|
||||
*/
|
||||
enum class BackendType {
|
||||
OpenGL, // OpenGL 4.x
|
||||
Vulkan, // Vulkan 1.x
|
||||
Metal, // Metal (macOS/iOS)
|
||||
D3D11, // Direct3D 11
|
||||
D3D12, // Direct3D 12
|
||||
OpenGLES, // OpenGL ES (移动平台)
|
||||
Count
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 后端工厂类,用于创建渲染后端实例
|
||||
* @brief 图形后端工厂
|
||||
* 用于注册和创建图形渲染后端
|
||||
*/
|
||||
class BackendFactory {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取后端工厂单例
|
||||
* @return 后端工厂实例引用
|
||||
*/
|
||||
static BackendFactory& getInstance();
|
||||
using BackendFn = std::function<UniquePtr<RenderBackend>()>;
|
||||
|
||||
/**
|
||||
* @brief 创建渲染后端
|
||||
* @param type 后端类型
|
||||
* @return 渲染后端实例
|
||||
* @brief 注册图形后端
|
||||
* @param name 后端名称
|
||||
* @param backend 后端创建函数
|
||||
* @param windowBackends 支持的窗口后端名称列表
|
||||
*/
|
||||
UniquePtr<RenderBackend> createBackend(BackendType type);
|
||||
static void reg(const std::string &name, BackendFn backend,
|
||||
const std::vector<std::string> &windowBackends = {});
|
||||
|
||||
/**
|
||||
* @brief 创建渲染后端实例
|
||||
* @param name 后端名称
|
||||
* @return 渲染后端实例,如果后端不存在返回 nullptr
|
||||
*/
|
||||
static UniquePtr<RenderBackend> createBackend(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief 创建默认渲染后端
|
||||
* @return 默认渲染后端实例
|
||||
*/
|
||||
UniquePtr<RenderBackend> createDefaultBackend();
|
||||
static UniquePtr<RenderBackend> createDefaultBackend();
|
||||
|
||||
/**
|
||||
* @brief 检查后端是否可用
|
||||
* @param type 后端类型
|
||||
* @return 可用返回true,否则返回false
|
||||
* @brief 创建与指定窗口后端兼容的渲染后端
|
||||
* @param windowBackend 窗口后端名称
|
||||
* @return 兼容的渲染后端实例
|
||||
*/
|
||||
bool isBackendAvailable(BackendType type) const;
|
||||
static UniquePtr<RenderBackend>
|
||||
createBackendForWindow(const std::string &windowBackend);
|
||||
|
||||
/**
|
||||
* @brief 获取当前平台推荐的后端类型
|
||||
* @return 推荐的后端类型
|
||||
* @brief 获取所有已注册的后端名称
|
||||
*/
|
||||
BackendType getRecommendedBackend() const;
|
||||
static std::vector<std::string> backends();
|
||||
|
||||
/**
|
||||
* @brief 获取后端类型名称
|
||||
* @param type 后端类型
|
||||
* @return 后端类型名称字符串
|
||||
* @brief 检查后端是否存在
|
||||
*/
|
||||
const char* getBackendName(BackendType type) const;
|
||||
static bool has(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief 从名称解析后端类型
|
||||
* @param name 后端类型名称
|
||||
* @return 后端类型,如果未知则返回OpenGL
|
||||
* @brief 获取推荐的后端名称
|
||||
* @return 推荐的后端名称
|
||||
*/
|
||||
BackendType parseBackendType(const char* name) const;
|
||||
static std::string getRecommendedBackend();
|
||||
|
||||
/**
|
||||
* @brief 获取与指定窗口后端兼容的推荐图形后端
|
||||
* @param windowBackend 窗口后端名称
|
||||
* @return 推荐的图形后端名称
|
||||
*/
|
||||
static std::string
|
||||
getRecommendedBackendForWindow(const std::string &windowBackend);
|
||||
|
||||
/**
|
||||
* @brief 检查图形后端是否支持指定窗口后端
|
||||
* @param graphicsBackend 图形后端名称
|
||||
* @param windowBackend 窗口后端名称
|
||||
* @return 支持返回 true
|
||||
*/
|
||||
static bool isCompatible(const std::string &graphicsBackend,
|
||||
const std::string &windowBackend);
|
||||
|
||||
/**
|
||||
* @brief 获取图形后端支持的窗口后端列表
|
||||
* @param graphicsBackend 图形后端名称
|
||||
* @return 支持的窗口后端列表
|
||||
*/
|
||||
static std::vector<std::string>
|
||||
getSupportedWindowBackends(const std::string &graphicsBackend);
|
||||
|
||||
private:
|
||||
BackendFactory() = default;
|
||||
~BackendFactory() = default;
|
||||
BackendFactory(const BackendFactory&) = delete;
|
||||
BackendFactory& operator=(const BackendFactory&) = delete;
|
||||
struct BackendEntry {
|
||||
BackendFn createFn;
|
||||
std::vector<std::string> windowBackends;
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, BackendEntry> ®istry();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 图形后端注册宏
|
||||
* 在全局作用域使用此宏注册图形后端
|
||||
*
|
||||
* @example
|
||||
* E2D_REG_GRAPHICS_BACKEND(opengl, GLRenderer, {"sdl2", "glfw"})
|
||||
*/
|
||||
#define E2D_REG_GRAPHICS_BACKEND(name, RendererClass, windowBackends) \
|
||||
namespace { \
|
||||
__attribute__((used)) static struct E2D_GRAPHICS_BACKEND_REG_##name { \
|
||||
E2D_GRAPHICS_BACKEND_REG_##name() { \
|
||||
::extra2d::graphics::BackendFactory::reg( \
|
||||
#name, \
|
||||
[]() -> ::extra2d::UniquePtr<::extra2d::RenderBackend> { \
|
||||
return ::extra2d::makeUnique<RendererClass>(); \
|
||||
}, \
|
||||
windowBackends); \
|
||||
} \
|
||||
} e2d_graphics_backend_reg_##name; \
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -14,17 +14,6 @@ class Texture;
|
|||
class FontAtlas;
|
||||
class Shader;
|
||||
|
||||
// ============================================================================
|
||||
// 渲染后端类型
|
||||
// ============================================================================
|
||||
enum class BackendType {
|
||||
OpenGL,
|
||||
// Vulkan,
|
||||
// Metal,
|
||||
// D3D11,
|
||||
// D3D12
|
||||
};
|
||||
|
||||
// BlendMode 定义在 pipeline.h 中
|
||||
|
||||
// ============================================================================
|
||||
|
|
@ -131,11 +120,6 @@ public:
|
|||
};
|
||||
virtual Stats getStats() const = 0;
|
||||
virtual void resetStats() = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 工厂方法
|
||||
// ------------------------------------------------------------------------
|
||||
static UniquePtr<RenderBackend> create(BackendType type);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@ namespace extra2d {
|
|||
* @brief 渲染模块配置结构
|
||||
*/
|
||||
struct RenderCfg {
|
||||
BackendType backend;
|
||||
std::string backend;
|
||||
int targetFPS;
|
||||
bool vsync;
|
||||
int multisamples;
|
||||
int priority;
|
||||
|
||||
RenderCfg()
|
||||
: backend(BackendType::OpenGL)
|
||||
: backend("")
|
||||
, targetFPS(60)
|
||||
, vsync(true)
|
||||
, multisamples(0)
|
||||
|
|
@ -29,7 +29,7 @@ struct RenderCfg {
|
|||
|
||||
/**
|
||||
* @brief 渲染模块
|
||||
* 管理渲染后端
|
||||
* 管理渲染后端,自动根据窗口后端选择兼容的渲染器
|
||||
*/
|
||||
class RenderModule : public Module {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <extra2d/platform/iinput.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
namespace platform {
|
||||
|
||||
/**
|
||||
* @brief 平台后端工厂
|
||||
|
|
@ -69,20 +70,19 @@ private:
|
|||
*/
|
||||
#define E2D_REG_BACKEND(name, WinClass, InClass) \
|
||||
namespace { \
|
||||
__attribute__((used)) \
|
||||
static struct E2D_BACKEND_REG_##name { \
|
||||
__attribute__((used)) static struct E2D_BACKEND_REG_##name { \
|
||||
E2D_BACKEND_REG_##name() { \
|
||||
::extra2d::BackendFactory::reg( \
|
||||
::extra2d::platform::BackendFactory::reg( \
|
||||
#name, \
|
||||
[]() -> ::extra2d::UniquePtr<::extra2d::IWindow> { \
|
||||
return ::extra2d::makeUnique<WinClass>(); \
|
||||
}, \
|
||||
[]() -> ::extra2d::UniquePtr<::extra2d::IInput> { \
|
||||
return ::extra2d::makeUnique<InClass>(); \
|
||||
} \
|
||||
); \
|
||||
}); \
|
||||
} \
|
||||
} e2d_backend_reg_##name; \
|
||||
}
|
||||
|
||||
} // namespace platform
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/platform/window_config.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -33,10 +32,13 @@ public:
|
|||
|
||||
/**
|
||||
* @brief 创建窗口
|
||||
* @param cfg 窗口配置
|
||||
* @param title 窗口标题
|
||||
* @param width 窗口宽度
|
||||
* @param height 窗口高度
|
||||
* @param vsync 是否启用垂直同步
|
||||
* @return 创建是否成功
|
||||
*/
|
||||
virtual bool create(const WindowConfigData& cfg) = 0;
|
||||
virtual bool create(const std::string& title, int width, int height, bool vsync = true) = 0;
|
||||
|
||||
/**
|
||||
* @brief 销毁窗口
|
||||
|
|
|
|||
|
|
@ -1,84 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @file window_config.h
|
||||
* @brief 窗口模块配置
|
||||
*
|
||||
* 定义窗口相关的配置数据结构,由 WindowModule 管理。
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 窗口模式枚举
|
||||
*/
|
||||
enum class WindowMode {
|
||||
Windowed,
|
||||
Fullscreen,
|
||||
Borderless
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 窗口配置数据结构
|
||||
*/
|
||||
struct WindowConfigData {
|
||||
std::string title = "Extra2D Application";
|
||||
int width = 1280;
|
||||
int height = 720;
|
||||
int minWidth = 320;
|
||||
int minHeight = 240;
|
||||
int maxWidth = 0;
|
||||
int maxHeight = 0;
|
||||
WindowMode mode = WindowMode::Windowed;
|
||||
bool resizable = true;
|
||||
bool borderless = false;
|
||||
bool alwaysOnTop = false;
|
||||
bool centered = true;
|
||||
int posX = -1;
|
||||
int posY = -1;
|
||||
bool hideOnClose = false;
|
||||
bool minimizeOnClose = true;
|
||||
float opacity = 1.0f;
|
||||
bool transparentFramebuffer = false;
|
||||
bool highDPI = true;
|
||||
float contentScale = 1.0f;
|
||||
bool vsync = true;
|
||||
int multisamples = 0;
|
||||
bool visible = true;
|
||||
bool decorated = true;
|
||||
|
||||
/**
|
||||
* @brief 检查窗口尺寸是否有效
|
||||
* @return 如果宽高都大于0返回 true
|
||||
*/
|
||||
bool isSizeValid() const { return width > 0 && height > 0; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否设置了窗口位置
|
||||
* @return 如果设置了有效位置返回 true
|
||||
*/
|
||||
bool hasPosition() const { return posX >= 0 && posY >= 0; }
|
||||
|
||||
/**
|
||||
* @brief 获取窗口宽高比
|
||||
* @return 宽高比值
|
||||
*/
|
||||
float aspectRatio() const { return static_cast<float>(width) / static_cast<float>(height); }
|
||||
|
||||
/**
|
||||
* @brief 检查是否为全屏模式
|
||||
* @return 如果是全屏模式返回 true
|
||||
*/
|
||||
bool isFullscreen() const { return mode == WindowMode::Fullscreen; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否为无边框模式
|
||||
* @return 如果是无边框模式返回 true
|
||||
*/
|
||||
bool isBorderless() const { return mode == WindowMode::Borderless || borderless; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <extra2d/platform/window_config.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -15,14 +14,13 @@ struct WindowCfg {
|
|||
std::string title;
|
||||
int w;
|
||||
int h;
|
||||
WindowMode mode;
|
||||
bool vsync;
|
||||
int priority;
|
||||
std::string backend;
|
||||
|
||||
WindowCfg()
|
||||
: title("Extra2D"), w(1280), h(720), mode(WindowMode::Windowed),
|
||||
vsync(true), priority(0), backend("sdl2") {}
|
||||
: title("Extra2D"), w(1280), h(720), vsync(true), priority(0),
|
||||
backend("sdl2") {}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -54,11 +52,16 @@ public:
|
|||
*/
|
||||
IWindow *win() const { return win_.get(); }
|
||||
|
||||
/**
|
||||
* @brief 获取窗口后端名称
|
||||
* @return 窗口后端名称
|
||||
*/
|
||||
const std::string &getWindowBackend() const { return cfg_.backend; }
|
||||
|
||||
private:
|
||||
WindowCfg cfg_;
|
||||
UniquePtr<IWindow> win_;
|
||||
bool initialized_ = false;
|
||||
bool sdlInited_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,127 +1,136 @@
|
|||
#include <extra2d/graphics/backends/backend_factory.h>
|
||||
|
||||
// 条件编译包含对应后端实现
|
||||
#ifdef E2D_BACKEND_OPENGL
|
||||
#include <extra2d/graphics/backends/opengl/gl_renderer.h>
|
||||
#endif
|
||||
|
||||
#ifdef E2D_BACKEND_VULKAN
|
||||
#include <extra2d/graphics/backends/vulkan/vk_renderer.h>
|
||||
#endif
|
||||
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace extra2d {
|
||||
namespace graphics {
|
||||
|
||||
BackendFactory& BackendFactory::getInstance() {
|
||||
static BackendFactory instance;
|
||||
return instance;
|
||||
std::unordered_map<std::string, BackendFactory::BackendEntry>& BackendFactory::registry() {
|
||||
static std::unordered_map<std::string, BackendEntry> reg;
|
||||
return reg;
|
||||
}
|
||||
|
||||
UniquePtr<RenderBackend> BackendFactory::createBackend(BackendType type) {
|
||||
switch (type) {
|
||||
#ifdef E2D_BACKEND_OPENGL
|
||||
case BackendType::OpenGL:
|
||||
E2D_LOG_INFO("Creating OpenGL render backend");
|
||||
return makeUnique<GLRenderer>();
|
||||
#endif
|
||||
void BackendFactory::reg(const std::string& name, BackendFn backend,
|
||||
const std::vector<std::string>& windowBackends) {
|
||||
registry()[name] = {backend, windowBackends};
|
||||
E2D_LOG_DEBUG("Registered graphics backend: {} (window backends: {})",
|
||||
name, windowBackends.size());
|
||||
}
|
||||
|
||||
#ifdef E2D_BACKEND_VULKAN
|
||||
case BackendType::Vulkan:
|
||||
E2D_LOG_INFO("Creating Vulkan render backend");
|
||||
return makeUnique<VulkanRenderer>();
|
||||
#endif
|
||||
|
||||
default:
|
||||
E2D_LOG_ERROR("Unsupported render backend type: {}", static_cast<int>(type));
|
||||
UniquePtr<RenderBackend> BackendFactory::createBackend(const std::string& name) {
|
||||
auto& reg = registry();
|
||||
auto it = reg.find(name);
|
||||
if (it != reg.end() && it->second.createFn) {
|
||||
E2D_LOG_INFO("Creating graphics backend: {}", name);
|
||||
return it->second.createFn();
|
||||
}
|
||||
E2D_LOG_ERROR("Graphics backend '{}' not found", name);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
UniquePtr<RenderBackend> BackendFactory::createDefaultBackend() {
|
||||
BackendType recommended = getRecommendedBackend();
|
||||
std::string recommended = getRecommendedBackend();
|
||||
if (recommended.empty()) {
|
||||
E2D_LOG_ERROR("No graphics backend available");
|
||||
return nullptr;
|
||||
}
|
||||
return createBackend(recommended);
|
||||
}
|
||||
|
||||
bool BackendFactory::isBackendAvailable(BackendType type) const {
|
||||
switch (type) {
|
||||
#ifdef E2D_BACKEND_OPENGL
|
||||
case BackendType::OpenGL:
|
||||
return true;
|
||||
#endif
|
||||
UniquePtr<RenderBackend> BackendFactory::createBackendForWindow(const std::string& windowBackend) {
|
||||
std::string recommended = getRecommendedBackendForWindow(windowBackend);
|
||||
if (recommended.empty()) {
|
||||
E2D_LOG_ERROR("No compatible graphics backend for window backend: {}", windowBackend);
|
||||
return nullptr;
|
||||
}
|
||||
return createBackend(recommended);
|
||||
}
|
||||
|
||||
#ifdef E2D_BACKEND_VULKAN
|
||||
case BackendType::Vulkan:
|
||||
return true;
|
||||
#endif
|
||||
std::vector<std::string> BackendFactory::backends() {
|
||||
std::vector<std::string> result;
|
||||
for (const auto& pair : registry()) {
|
||||
result.push_back(pair.first);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
default:
|
||||
bool BackendFactory::has(const std::string& name) {
|
||||
return registry().find(name) != registry().end();
|
||||
}
|
||||
|
||||
std::string BackendFactory::getRecommendedBackend() {
|
||||
auto& reg = registry();
|
||||
|
||||
static const std::vector<std::string> priority = {
|
||||
"vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles"
|
||||
};
|
||||
|
||||
for (const auto& name : priority) {
|
||||
if (reg.find(name) != reg.end()) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
if (!reg.empty()) {
|
||||
return reg.begin()->first;
|
||||
}
|
||||
|
||||
E2D_LOG_WARN("No graphics backend registered");
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string BackendFactory::getRecommendedBackendForWindow(const std::string& windowBackend) {
|
||||
auto& reg = registry();
|
||||
|
||||
static const std::vector<std::string> priority = {
|
||||
"vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles"
|
||||
};
|
||||
|
||||
for (const auto& name : priority) {
|
||||
auto it = reg.find(name);
|
||||
if (it != reg.end() && isCompatible(name, windowBackend)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& pair : reg) {
|
||||
if (isCompatible(pair.first, windowBackend)) {
|
||||
return pair.first;
|
||||
}
|
||||
}
|
||||
|
||||
E2D_LOG_WARN("No compatible graphics backend for window backend: {}", windowBackend);
|
||||
return "";
|
||||
}
|
||||
|
||||
bool BackendFactory::isCompatible(const std::string& graphicsBackend, const std::string& windowBackend) {
|
||||
auto& reg = registry();
|
||||
auto it = reg.find(graphicsBackend);
|
||||
if (it == reg.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& windowBackends = it->second.windowBackends;
|
||||
if (windowBackends.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BackendType BackendFactory::getRecommendedBackend() const {
|
||||
// 平台特定的默认后端选择
|
||||
// 优先级:Vulkan > OpenGL
|
||||
|
||||
#ifdef E2D_BACKEND_VULKAN
|
||||
return BackendType::Vulkan;
|
||||
#endif
|
||||
|
||||
#ifdef E2D_BACKEND_OPENGL
|
||||
return BackendType::OpenGL;
|
||||
#endif
|
||||
|
||||
// 如果没有可用的后端,返回OpenGL作为默认值
|
||||
return BackendType::OpenGL;
|
||||
}
|
||||
|
||||
const char* BackendFactory::getBackendName(BackendType type) const {
|
||||
switch (type) {
|
||||
case BackendType::OpenGL:
|
||||
return "OpenGL";
|
||||
case BackendType::Vulkan:
|
||||
return "Vulkan";
|
||||
case BackendType::Metal:
|
||||
return "Metal";
|
||||
case BackendType::D3D11:
|
||||
return "D3D11";
|
||||
case BackendType::D3D12:
|
||||
return "D3D12";
|
||||
case BackendType::OpenGLES:
|
||||
return "OpenGL ES";
|
||||
default:
|
||||
return "Unknown";
|
||||
for (const auto& wb : windowBackends) {
|
||||
if (wb == windowBackend) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
BackendType BackendFactory::parseBackendType(const char* name) const {
|
||||
if (!name) {
|
||||
return BackendType::OpenGL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::strcmp(name, "opengl") == 0 || std::strcmp(name, "OpenGL") == 0) {
|
||||
return BackendType::OpenGL;
|
||||
std::vector<std::string> BackendFactory::getSupportedWindowBackends(const std::string& graphicsBackend) {
|
||||
auto& reg = registry();
|
||||
auto it = reg.find(graphicsBackend);
|
||||
if (it != reg.end()) {
|
||||
return it->second.windowBackends;
|
||||
}
|
||||
if (std::strcmp(name, "vulkan") == 0 || std::strcmp(name, "Vulkan") == 0) {
|
||||
return BackendType::Vulkan;
|
||||
}
|
||||
if (std::strcmp(name, "metal") == 0 || std::strcmp(name, "Metal") == 0) {
|
||||
return BackendType::Metal;
|
||||
}
|
||||
if (std::strcmp(name, "d3d11") == 0 || std::strcmp(name, "D3D11") == 0) {
|
||||
return BackendType::D3D11;
|
||||
}
|
||||
if (std::strcmp(name, "d3d12") == 0 || std::strcmp(name, "D3D12") == 0) {
|
||||
return BackendType::D3D12;
|
||||
}
|
||||
if (std::strcmp(name, "opengles") == 0 || std::strcmp(name, "OpenGLES") == 0) {
|
||||
return BackendType::OpenGLES;
|
||||
}
|
||||
|
||||
E2D_LOG_WARN("Unknown backend type '{}', defaulting to OpenGL", name);
|
||||
return BackendType::OpenGL;
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
#include <extra2d/graphics/backends/opengl/gl_renderer.h>
|
||||
#include <extra2d/graphics/backends/backend_factory.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace graphics {
|
||||
|
||||
namespace {
|
||||
static bool s_openglBackendRegistered = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化 OpenGL 后端注册
|
||||
*/
|
||||
void initOpenGLBackend() {
|
||||
if (s_openglBackendRegistered) {
|
||||
return;
|
||||
}
|
||||
s_openglBackendRegistered = true;
|
||||
|
||||
BackendFactory::reg(
|
||||
"opengl",
|
||||
[]() -> UniquePtr<RenderBackend> {
|
||||
return makeUnique<GLRenderer>();
|
||||
},
|
||||
{"sdl2", "glfw"}
|
||||
);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct OpenGLBackendAutoReg {
|
||||
OpenGLBackendAutoReg() {
|
||||
initOpenGLBackend();
|
||||
}
|
||||
};
|
||||
static OpenGLBackendAutoReg s_openglAutoReg;
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#include <extra2d/graphics/backends/vulkan/vk_renderer.h>
|
||||
#include <extra2d/graphics/backends/backend_factory.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace graphics {
|
||||
|
||||
namespace {
|
||||
static bool s_vulkanBackendRegistered = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化 Vulkan 后端注册
|
||||
*/
|
||||
void initVulkanBackend() {
|
||||
if (s_vulkanBackendRegistered) {
|
||||
return;
|
||||
}
|
||||
s_vulkanBackendRegistered = true;
|
||||
|
||||
BackendFactory::reg(
|
||||
"vulkan",
|
||||
[]() -> UniquePtr<RenderBackend> {
|
||||
return makeUnique<VulkanRenderer>();
|
||||
},
|
||||
{"sdl2", "glfw"}
|
||||
);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct VulkanBackendAutoReg {
|
||||
VulkanBackendAutoReg() {
|
||||
initVulkanBackend();
|
||||
}
|
||||
};
|
||||
static VulkanBackendAutoReg s_vulkanAutoReg;
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
#include <extra2d/graphics/backends/opengl/gl_renderer.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 创建渲染后端实例
|
||||
*
|
||||
* 根据指定的后端类型创建对应的渲染后端实例,
|
||||
* 目前支持OpenGL后端
|
||||
*
|
||||
* @param type 渲染后端类型(如OpenGL)
|
||||
* @return 成功返回渲染后端的唯一指针,不支持的类型返回nullptr
|
||||
*/
|
||||
UniquePtr<RenderBackend> RenderBackend::create(BackendType type) {
|
||||
switch (type) {
|
||||
case BackendType::OpenGL:
|
||||
return makeUnique<GLRenderer>();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#include <extra2d/graphics/core/render_module.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_renderer.h>
|
||||
#include <extra2d/graphics/backends/backend_factory.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_shader.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/core/registry.h>
|
||||
|
|
@ -11,6 +11,16 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明后端初始化函数
|
||||
namespace graphics {
|
||||
#ifdef E2D_BACKEND_OPENGL
|
||||
void initOpenGLBackend();
|
||||
#endif
|
||||
#ifdef E2D_BACKEND_VULKAN
|
||||
void initVulkanBackend();
|
||||
#endif
|
||||
}
|
||||
|
||||
RenderModule::RenderModule(std::function<void(RenderCfg&)> configFn) {
|
||||
configFn(cfg_);
|
||||
}
|
||||
|
|
@ -33,13 +43,20 @@ static std::string getExecutableDir() {
|
|||
bool RenderModule::init() {
|
||||
if (initialized_) return true;
|
||||
|
||||
// 获取WindowModule依赖
|
||||
auto* winMod = Registry::instance().get<WindowModule>();
|
||||
if (!winMod || !winMod->win()) {
|
||||
E2D_LOG_ERROR("WindowModule not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 初始化ShaderManager
|
||||
// 初始化图形后端(注册到工厂)
|
||||
#ifdef E2D_BACKEND_OPENGL
|
||||
graphics::initOpenGLBackend();
|
||||
#endif
|
||||
#ifdef E2D_BACKEND_VULKAN
|
||||
graphics::initVulkanBackend();
|
||||
#endif
|
||||
|
||||
if (!ShaderManager::getInstance().isInitialized()) {
|
||||
auto factory = makeShared<GLShaderFactory>();
|
||||
std::string shaderDir = getExecutableDir() + "shaders/";
|
||||
|
|
@ -49,18 +66,31 @@ bool RenderModule::init() {
|
|||
}
|
||||
}
|
||||
|
||||
// 创建渲染后端
|
||||
renderer_ = RenderBackend::create(cfg_.backend);
|
||||
std::string windowBackend = winMod->getWindowBackend();
|
||||
|
||||
if (cfg_.backend.empty()) {
|
||||
E2D_LOG_INFO("No graphics backend specified, auto-selecting for window backend: {}", windowBackend);
|
||||
renderer_ = graphics::BackendFactory::createBackendForWindow(windowBackend);
|
||||
} else {
|
||||
if (!graphics::BackendFactory::isCompatible(cfg_.backend, windowBackend)) {
|
||||
E2D_LOG_WARN("Graphics backend '{}' is not compatible with window backend '{}'",
|
||||
cfg_.backend, windowBackend);
|
||||
}
|
||||
renderer_ = graphics::BackendFactory::createBackend(cfg_.backend);
|
||||
}
|
||||
|
||||
if (!renderer_) {
|
||||
E2D_LOG_ERROR("Failed to create render backend");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 初始化渲染器
|
||||
if (!renderer_->init(winMod->win())) {
|
||||
E2D_LOG_ERROR("Failed to initialize render backend");
|
||||
renderer_.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("Render module initialized successfully");
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include <extra2d/platform/backend_factory.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace platform {
|
||||
|
||||
std::unordered_map<std::string, BackendFactory::BackendEntry>& BackendFactory::registry() {
|
||||
static std::unordered_map<std::string, BackendEntry> reg;
|
||||
|
|
@ -41,4 +42,5 @@ bool BackendFactory::has(const std::string& name) {
|
|||
return registry().find(name) != registry().end();
|
||||
}
|
||||
|
||||
} // namespace platform
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <extra2d/platform/backend_factory.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace platform {
|
||||
|
||||
namespace {
|
||||
static bool s_glfwBackendRegistered = false;
|
||||
|
|
@ -25,4 +26,5 @@ void initGLFWBackend() {
|
|||
);
|
||||
}
|
||||
|
||||
} // namespace platform
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -11,12 +11,11 @@ GLFWWindow::~GLFWWindow() {
|
|||
destroy();
|
||||
}
|
||||
|
||||
bool GLFWWindow::create(const WindowConfigData& cfg) {
|
||||
bool GLFWWindow::create(const std::string& title, int width, int height, bool vsync) {
|
||||
if (!initGLFW()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置 OpenGL ES 3.2 上下文
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
|
|
@ -26,44 +25,21 @@ bool GLFWWindow::create(const WindowConfigData& cfg) {
|
|||
glfwWindowHint(GLFW_STENCIL_BITS, 8);
|
||||
|
||||
#ifdef __SWITCH__
|
||||
// Switch 平台强制全屏
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
|
||||
fullscreen_ = true;
|
||||
#else
|
||||
// 桌面平台配置
|
||||
if (cfg.resizable) {
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
||||
} else {
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||
}
|
||||
|
||||
if (!cfg.decorated) {
|
||||
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
|
||||
}
|
||||
|
||||
if (cfg.isFullscreen()) {
|
||||
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
|
||||
fullscreen_ = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cfg.multisamples > 0) {
|
||||
glfwWindowHint(GLFW_SAMPLES, cfg.multisamples);
|
||||
}
|
||||
|
||||
// 创建窗口
|
||||
GLFWmonitor* monitor = nullptr;
|
||||
#ifdef __SWITCH__
|
||||
monitor = glfwGetPrimaryMonitor();
|
||||
#endif
|
||||
if (fullscreen_ && !monitor) {
|
||||
monitor = glfwGetPrimaryMonitor();
|
||||
}
|
||||
|
||||
glfwWindow_ = glfwCreateWindow(
|
||||
cfg.width, cfg.height,
|
||||
cfg.title.c_str(),
|
||||
width, height,
|
||||
title.c_str(),
|
||||
monitor,
|
||||
nullptr
|
||||
);
|
||||
|
|
@ -74,7 +50,6 @@ bool GLFWWindow::create(const WindowConfigData& cfg) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 窗口居中(非全屏模式下)
|
||||
#ifndef __SWITCH__
|
||||
if (!fullscreen_ && !monitor) {
|
||||
GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor();
|
||||
|
|
@ -83,8 +58,8 @@ bool GLFWWindow::create(const WindowConfigData& cfg) {
|
|||
if (mode) {
|
||||
int screenWidth = mode->width;
|
||||
int screenHeight = mode->height;
|
||||
int windowX = (screenWidth - cfg.width) / 2;
|
||||
int windowY = (screenHeight - cfg.height) / 2;
|
||||
int windowX = (screenWidth - width) / 2;
|
||||
int windowY = (screenHeight - height) / 2;
|
||||
glfwSetWindowPos(glfwWindow_, windowX, windowY);
|
||||
}
|
||||
}
|
||||
|
|
@ -93,7 +68,6 @@ bool GLFWWindow::create(const WindowConfigData& cfg) {
|
|||
|
||||
glfwMakeContextCurrent(glfwWindow_);
|
||||
|
||||
// 初始化 GLAD
|
||||
if (!gladLoadGLES2Loader((GLADloadproc)glfwGetProcAddress)) {
|
||||
E2D_LOG_ERROR("Failed to initialize GLAD GLES2");
|
||||
glfwDestroyWindow(glfwWindow_);
|
||||
|
|
@ -102,15 +76,12 @@ bool GLFWWindow::create(const WindowConfigData& cfg) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 设置垂直同步
|
||||
glfwSwapInterval(cfg.vsync ? 1 : 0);
|
||||
vsync_ = cfg.vsync;
|
||||
glfwSwapInterval(vsync ? 1 : 0);
|
||||
vsync_ = vsync;
|
||||
|
||||
// 获取实际窗口大小
|
||||
glfwGetWindowSize(glfwWindow_, &width_, &height_);
|
||||
updateContentScale();
|
||||
|
||||
// 设置回调函数
|
||||
glfwSetWindowUserPointer(glfwWindow_, this);
|
||||
glfwSetFramebufferSizeCallback(glfwWindow_, framebufferSizeCallback);
|
||||
glfwSetWindowCloseCallback(glfwWindow_, windowCloseCallback);
|
||||
|
|
@ -122,7 +93,6 @@ bool GLFWWindow::create(const WindowConfigData& cfg) {
|
|||
glfwSetKeyCallback(glfwWindow_, keyCallback);
|
||||
glfwSetJoystickCallback(joystickCallback);
|
||||
|
||||
// 创建输入系统
|
||||
input_ = makeUnique<GLFWInput>();
|
||||
input_->setWindow(glfwWindow_);
|
||||
input_->init();
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public:
|
|||
GLFWWindow();
|
||||
~GLFWWindow() override;
|
||||
|
||||
bool create(const WindowConfigData& cfg) override;
|
||||
bool create(const std::string& title, int width, int height, bool vsync = true) override;
|
||||
void destroy() override;
|
||||
|
||||
void poll() override;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <extra2d/platform/backend_factory.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace platform {
|
||||
|
||||
namespace {
|
||||
static bool s_sdl2BackendRegistered = false;
|
||||
|
|
@ -25,4 +26,5 @@ void initSDL2Backend() {
|
|||
);
|
||||
}
|
||||
|
||||
} // namespace platform
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -15,27 +15,15 @@ SDL2Window::~SDL2Window() {
|
|||
destroy();
|
||||
}
|
||||
|
||||
bool SDL2Window::create(const WindowConfigData& cfg) {
|
||||
bool SDL2Window::create(const std::string& title, int width, int height, bool vsync) {
|
||||
if (!initSDL()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
|
||||
Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
|
||||
|
||||
#ifdef __SWITCH__
|
||||
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
#else
|
||||
if (cfg.isFullscreen()) {
|
||||
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
} else if (cfg.isBorderless()) {
|
||||
flags |= SDL_WINDOW_BORDERLESS;
|
||||
}
|
||||
if (cfg.resizable) {
|
||||
flags |= SDL_WINDOW_RESIZABLE;
|
||||
}
|
||||
if (!cfg.decorated) {
|
||||
flags |= SDL_WINDOW_BORDERLESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
|
|
@ -46,22 +34,10 @@ bool SDL2Window::create(const WindowConfigData& cfg) {
|
|||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
|
||||
if (cfg.multisamples > 0) {
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, cfg.multisamples);
|
||||
}
|
||||
|
||||
int x = SDL_WINDOWPOS_CENTERED;
|
||||
int y = SDL_WINDOWPOS_CENTERED;
|
||||
if (!cfg.centered) {
|
||||
x = SDL_WINDOWPOS_UNDEFINED;
|
||||
y = SDL_WINDOWPOS_UNDEFINED;
|
||||
}
|
||||
|
||||
sdlWindow_ = SDL_CreateWindow(
|
||||
cfg.title.c_str(),
|
||||
x, y,
|
||||
cfg.width, cfg.height,
|
||||
title.c_str(),
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
width, height,
|
||||
flags
|
||||
);
|
||||
|
||||
|
|
@ -90,11 +66,11 @@ bool SDL2Window::create(const WindowConfigData& cfg) {
|
|||
return false;
|
||||
}
|
||||
|
||||
SDL_GL_SetSwapInterval(cfg.vsync ? 1 : 0);
|
||||
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
|
||||
|
||||
SDL_GetWindowSize(sdlWindow_, &width_, &height_);
|
||||
fullscreen_ = (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
|
||||
vsync_ = cfg.vsync;
|
||||
vsync_ = vsync;
|
||||
|
||||
#ifndef __SWITCH__
|
||||
initCursors();
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public:
|
|||
SDL2Window();
|
||||
~SDL2Window() override;
|
||||
|
||||
bool create(const WindowConfigData& cfg) override;
|
||||
bool create(const std::string& title, int width, int height, bool vsync = true) override;
|
||||
void destroy() override;
|
||||
|
||||
void poll() override;
|
||||
|
|
|
|||
|
|
@ -10,11 +10,13 @@
|
|||
namespace extra2d {
|
||||
|
||||
// 前向声明后端初始化函数
|
||||
namespace platform {
|
||||
#if defined(E2D_BACKEND_SDL2)
|
||||
void initSDL2Backend();
|
||||
#elif defined(E2D_BACKEND_GLFW)
|
||||
void initGLFWBackend();
|
||||
#endif
|
||||
}
|
||||
|
||||
WindowModule::WindowModule(std::function<void(WindowCfg&)> configFn) {
|
||||
configFn(cfg_);
|
||||
|
|
@ -29,39 +31,27 @@ WindowModule::~WindowModule() {
|
|||
bool WindowModule::init() {
|
||||
if (initialized_) return true;
|
||||
|
||||
#ifdef __SWITCH__
|
||||
cfg_.mode = WindowMode::Fullscreen;
|
||||
#endif
|
||||
|
||||
// 初始化后端(注册到工厂)
|
||||
#if defined(E2D_BACKEND_SDL2)
|
||||
initSDL2Backend();
|
||||
platform::initSDL2Backend();
|
||||
#elif defined(E2D_BACKEND_GLFW)
|
||||
initGLFWBackend();
|
||||
platform::initGLFWBackend();
|
||||
#else
|
||||
#error "No window backend defined"
|
||||
#endif
|
||||
|
||||
E2D_LOG_INFO("Window backend initialized");
|
||||
|
||||
// 创建窗口配置
|
||||
WindowConfigData winCfg;
|
||||
winCfg.title = cfg_.title;
|
||||
winCfg.width = cfg_.w;
|
||||
winCfg.height = cfg_.h;
|
||||
winCfg.mode = cfg_.mode;
|
||||
winCfg.vsync = cfg_.vsync;
|
||||
|
||||
E2D_LOG_INFO("Creating window with size {}x{}", cfg_.w, cfg_.h);
|
||||
|
||||
// 创建窗口(使用配置的后端)
|
||||
win_ = BackendFactory::createWindow(cfg_.backend);
|
||||
win_ = platform::BackendFactory::createWindow(cfg_.backend);
|
||||
if (!win_) {
|
||||
E2D_LOG_ERROR("Failed to create window backend: {}", cfg_.backend);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!win_->create(winCfg)) {
|
||||
if (!win_->create(cfg_.title, cfg_.w, cfg_.h, cfg_.vsync)) {
|
||||
E2D_LOG_ERROR("Failed to create window");
|
||||
shutdown();
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -3,25 +3,27 @@
|
|||
* @brief Extra2D 场景图测试示例
|
||||
*/
|
||||
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/extra2d.h>
|
||||
#include <extra2d/graphics/core/render_module.h>
|
||||
#include <extra2d/platform/input_module.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/services/camera_service.h>
|
||||
#include <extra2d/services/event_service.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace extra2d;
|
||||
|
||||
void createSceneGraph(Scene *scene) {
|
||||
float width = scene->getWidth();
|
||||
float height = scene->getHeight();
|
||||
/**
|
||||
* @brief 基础场景类,演示场景图功能
|
||||
*/
|
||||
class BasicScene : public Scene {
|
||||
public:
|
||||
/**
|
||||
* @brief 场景进入时初始化场景图
|
||||
*/
|
||||
void onEnter() override {
|
||||
float width = getWidth();
|
||||
float height = getHeight();
|
||||
|
||||
auto root = makeShared<Node>();
|
||||
root->setName("Root");
|
||||
root->setPos(width / 2, height / 2);
|
||||
scene->addChild(root);
|
||||
addChild(root);
|
||||
|
||||
auto parent1 = makeShared<Node>();
|
||||
parent1->setName("Parent1");
|
||||
|
|
@ -61,7 +63,8 @@ void createSceneGraph(Scene *scene) {
|
|||
parent2->addChild(child2);
|
||||
|
||||
auto triangle = ShapeNode::createFilledTriangle(
|
||||
Vec2(0, -40), Vec2(-35, 30), Vec2(35, 30), Color(0.4f, 1.0f, 0.4f, 1.0f));
|
||||
Vec2(0, -40), Vec2(-35, 30), Vec2(35, 30),
|
||||
Color(0.4f, 1.0f, 0.4f, 1.0f));
|
||||
triangle->setName("GreenTriangle");
|
||||
child2->addChild(triangle);
|
||||
|
||||
|
|
@ -76,23 +79,20 @@ void createSceneGraph(Scene *scene) {
|
|||
polygon->setName("PurplePolygon");
|
||||
polygon->setPos(0, -150);
|
||||
root->addChild(polygon);
|
||||
|
||||
std::cout << "\n=== Scene Graph Structure ===" << std::endl;
|
||||
std::cout << "Scene (root)" << std::endl;
|
||||
std::cout << " └── Root (center)" << std::endl;
|
||||
std::cout << " ├── Parent1 (left)" << std::endl;
|
||||
std::cout << " │ ├── RedRect (100x100)" << std::endl;
|
||||
std::cout << " │ └── Child1 (rotated 45, scaled 0.5)" << std::endl;
|
||||
std::cout << " │ └── OrangeRect (60x60)" << std::endl;
|
||||
std::cout << " ├── Parent2 (right)" << std::endl;
|
||||
std::cout << " │ ├── BlueCircle (radius 60)" << std::endl;
|
||||
std::cout << " │ └── Child2 (below)" << std::endl;
|
||||
std::cout << " │ └── GreenTriangle" << std::endl;
|
||||
std::cout << " ├── BottomLine" << std::endl;
|
||||
std::cout << " └── PurplePolygon (pentagon)" << std::endl;
|
||||
std::cout << "=============================\n" << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 场景退出时清理资源
|
||||
*/
|
||||
void onExit() override { clearChildren(); }
|
||||
|
||||
/**
|
||||
* @brief 自定义渲染逻辑
|
||||
* @param renderer 渲染后端
|
||||
*/
|
||||
void onRender(RenderBackend &renderer) override { Scene::onRender(renderer); }
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
|
@ -101,7 +101,6 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
Application &app = Application::get();
|
||||
|
||||
// 注册模块(按优先级顺序)
|
||||
app.use<WindowModule>([](auto &cfg) {
|
||||
cfg.w = 1280;
|
||||
cfg.h = 720;
|
||||
|
|
@ -109,7 +108,10 @@ int main(int argc, char *argv[]) {
|
|||
cfg.backend = "glfw";
|
||||
});
|
||||
|
||||
app.use<RenderModule>([](auto &cfg) { cfg.priority = 10; });
|
||||
app.use<RenderModule>([](auto &cfg) {
|
||||
cfg.priority = 10;
|
||||
cfg.backend = "opengl";
|
||||
});
|
||||
|
||||
app.use<InputModule>([](auto &cfg) { cfg.priority = 20; });
|
||||
|
||||
|
|
@ -145,7 +147,7 @@ int main(int argc, char *argv[]) {
|
|||
});
|
||||
}
|
||||
|
||||
auto scene = Scene::create();
|
||||
auto scene = makeShared<BasicScene>();
|
||||
scene->setBackgroundColor(Color(0.12f, 0.12f, 0.16f, 1.0f));
|
||||
if (win) {
|
||||
scene->setViewportSize(static_cast<float>(win->width()),
|
||||
|
|
@ -163,7 +165,6 @@ int main(int argc, char *argv[]) {
|
|||
cameraService->applyViewportAdapter();
|
||||
}
|
||||
|
||||
createSceneGraph(scene.get());
|
||||
app.enterScene(scene);
|
||||
|
||||
std::cout << "\nControls:" << std::endl;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
#include "hello_module.h"
|
||||
#include <extra2d/app/application.h>
|
||||
#include <extra2d/graphics/core/render_module.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/scene/scene.h>
|
||||
#include <extra2d/services/scene_service.h>
|
||||
#include <extra2d/extra2d.h>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
using namespace extra2d;
|
||||
|
||||
class HelloScene : public Scene {
|
||||
|
|
@ -18,7 +13,7 @@ public:
|
|||
std::cout << "HelloScene entered" << std::endl;
|
||||
setBackgroundColor(Color(0.1f, 0.1f, 0.2f, 1.0f));
|
||||
|
||||
auto *hello = Application::get().get<HelloModule>();
|
||||
auto hello = Application::get().get<HelloModule>();
|
||||
if (hello) {
|
||||
std::cout << "Scene calling HelloModule from onEnter..." << std::endl;
|
||||
hello->sayHello();
|
||||
|
|
@ -44,13 +39,6 @@ private:
|
|||
};
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(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();
|
||||
|
||||
|
|
@ -71,21 +59,9 @@ int main(int argc, char *argv[]) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "" << std::endl;
|
||||
std::cout << "Application initialized successfully" << std::endl;
|
||||
std::cout << "" << std::endl;
|
||||
|
||||
auto scene = HelloScene::create();
|
||||
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();
|
||||
|
||||
std::cout << "Application shutting down..." << std::endl;
|
||||
app.shutdown();
|
||||
std::cout << "Application shutdown complete" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,15 +6,7 @@
|
|||
* 此示例不依赖任何特定渲染后端(如 OpenGL)
|
||||
*/
|
||||
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/extra2d.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/graphics/core/render_module.h>
|
||||
#include <extra2d/graphics/texture/font.h>
|
||||
#include <extra2d/platform/input_module.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/services/camera_service.h>
|
||||
#include <extra2d/services/event_service.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace extra2d;
|
||||
|
|
@ -103,9 +95,7 @@ public:
|
|||
// 注意:无需手动调用 renderer.endSpriteBatch(),帧结束时会自动刷新
|
||||
}
|
||||
|
||||
void setRenderer(RenderBackend* renderer) {
|
||||
renderer_ = renderer;
|
||||
}
|
||||
void setRenderer(RenderBackend *renderer) { renderer_ = renderer; }
|
||||
|
||||
private:
|
||||
void renderText(RenderBackend &renderer, const std::string &text, float x,
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ function define_extra2d_engine()
|
|||
|
||||
-- 渲染后端源文件
|
||||
local render_backend = get_render_backend()
|
||||
-- 图形后端工厂(始终编译)
|
||||
add_files("Extra2D/src/graphics/backends/backend_factory.cpp")
|
||||
if render_backend == "vulkan" then
|
||||
add_files("Extra2D/src/graphics/backends/vulkan/*.cpp")
|
||||
add_defines("E2D_BACKEND_VULKAN")
|
||||
|
|
|
|||
Loading…
Reference in New Issue