refactor(render): 移除多后端支持,仅保留GLFW和OpenGL

简化渲染模块和窗口模块,移除SDL2和Vulkan后端支持,仅保留GLFW窗口后端和OpenGL渲染后端。删除相关后端工厂代码和配置文件,修改模块初始化逻辑直接使用GLFW和OpenGL实现。

- 删除SDL2和Vulkan后端相关代码
- 移除后端工厂模式和相关注册逻辑
- 修改窗口模块直接使用GLFW实现
- 修改渲染模块直接使用OpenGL实现
- 更新构建配置仅支持GLFW+OpenGL组合
This commit is contained in:
ChestnutYueyue 2026-02-20 12:48:36 +08:00
parent 8e385beeae
commit b892736fb2
33 changed files with 39 additions and 2287 deletions

View File

@ -1,124 +0,0 @@
#pragma once
#include <extra2d/core/types.h>
#include <extra2d/graphics/core/render_backend.h>
#include <functional>
#include <string>
#include <unordered_map>
#include <vector>
namespace extra2d {
namespace graphics {
/**
* @brief
*
*/
class BackendFactory {
public:
using BackendFn = std::function<UniquePtr<RenderBackend>()>;
/**
* @brief
* @param name
* @param backend
* @param windowBackends
*/
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
*/
static UniquePtr<RenderBackend> createDefaultBackend();
/**
* @brief
* @param windowBackend
* @return
*/
static UniquePtr<RenderBackend>
createBackendForWindow(const std::string &windowBackend);
/**
* @brief
*/
static std::vector<std::string> backends();
/**
* @brief
*/
static bool has(const std::string &name);
/**
* @brief
* @return
*/
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:
struct BackendEntry {
BackendFn createFn;
std::vector<std::string> windowBackends;
};
static std::unordered_map<std::string, BackendEntry> &registry();
};
/**
* @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

View File

@ -1,78 +0,0 @@
#pragma once
#include <extra2d/graphics/core/render_backend.h>
namespace extra2d {
/**
* @brief Vulkan
*
* Vulkan后端应该包含的内容
* Vulkan上下文线
*/
class VulkanRenderer : public RenderBackend {
public:
VulkanRenderer();
~VulkanRenderer() override;
// RenderBackend 接口实现
bool init(IWindow* window) override;
void shutdown() override;
void beginFrame(const Color &clearColor) override;
void endFrame() override;
void setViewport(int x, int y, int width, int height) override;
void setVSync(bool enabled) override;
void setBlendMode(BlendMode mode) override;
void setViewProjection(const glm::mat4 &matrix) override;
void pushTransform(const glm::mat4 &transform) override;
void popTransform() override;
glm::mat4 getCurrentTransform() const override;
Ptr<Texture> createTexture(int width, int height, const uint8_t *pixels,
int channels) override;
Ptr<Texture> loadTexture(const std::string &filepath) override;
void beginSpriteBatch() override;
void drawSprite(const Texture &texture, const Rect &destRect,
const Rect &srcRect, const Color &tint, float rotation,
const Vec2 &anchor) override;
void drawSprite(const Texture &texture, const Vec2 &position,
const Color &tint) override;
void endSpriteBatch() override;
void drawLine(const Vec2 &start, const Vec2 &end, const Color &color,
float width) override;
void drawRect(const Rect &rect, const Color &color, float width) override;
void fillRect(const Rect &rect, const Color &color) override;
void drawCircle(const Vec2 &center, float radius, const Color &color,
int segments, float width) override;
void fillCircle(const Vec2 &center, float radius, const Color &color,
int segments) override;
void drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
const Color &color, float width) override;
void fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
const Color &color) override;
void drawPolygon(const std::vector<Vec2> &points, const Color &color,
float width) override;
void fillPolygon(const std::vector<Vec2> &points,
const Color &color) override;
Ptr<FontAtlas> createFontAtlas(const std::string &filepath, int fontSize,
bool useSDF = false) override;
void drawText(const FontAtlas &font, const std::string &text,
const Vec2 &position, const Color &color) override;
void drawText(const FontAtlas &font, const std::string &text, float x,
float y, const Color &color) override;
Stats getStats() const override { return stats_; }
void resetStats() override;
private:
Stats stats_;
bool initialized_ = false;
};
} // namespace extra2d

View File

@ -12,15 +12,13 @@ namespace extra2d {
* @brief * @brief
*/ */
struct RenderCfg { struct RenderCfg {
std::string backend;
int targetFPS; int targetFPS;
bool vsync; bool vsync;
int multisamples; int multisamples;
int priority; int priority;
RenderCfg() RenderCfg()
: backend("") : targetFPS(60)
, targetFPS(60)
, vsync(true) , vsync(true)
, multisamples(0) , multisamples(0)
, priority(10) , priority(10)
@ -29,7 +27,7 @@ struct RenderCfg {
/** /**
* @brief * @brief
* * OpenGL
*/ */
class RenderModule : public Module { class RenderModule : public Module {
public: public:

View File

@ -1,88 +0,0 @@
#pragma once
#include <extra2d/core/types.h>
#include <extra2d/platform/iinput.h>
#include <extra2d/platform/iwindow.h>
#include <functional>
#include <string>
#include <unordered_map>
#include <vector>
namespace extra2d {
namespace platform {
/**
* @brief
*
*/
class BackendFactory {
public:
using WindowFn = std::function<UniquePtr<IWindow>()>;
using InputFn = std::function<UniquePtr<IInput>()>;
/**
* @brief
* @param name
* @param win
* @param in
*/
static void reg(const std::string &name, WindowFn win, InputFn in);
/**
* @brief
* @param name
* @return nullptr
*/
static UniquePtr<IWindow> createWindow(const std::string &name);
/**
* @brief
* @param name
* @return nullptr
*/
static UniquePtr<IInput> createInput(const std::string &name);
/**
* @brief
*/
static std::vector<std::string> backends();
/**
* @brief
*/
static bool has(const std::string &name);
private:
struct BackendEntry {
WindowFn windowFn;
InputFn inputFn;
};
static std::unordered_map<std::string, BackendEntry> &registry();
};
/**
* @brief
* 使
*
* @example
* E2D_REG_BACKEND(sdl2, SDL2Window, SDL2Input)
*/
#define E2D_REG_BACKEND(name, WinClass, InClass) \
namespace { \
__attribute__((used)) static struct E2D_BACKEND_REG_##name { \
E2D_BACKEND_REG_##name() { \
::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

View File

@ -16,11 +16,9 @@ struct WindowCfg {
int h; int h;
bool vsync; bool vsync;
int priority; int priority;
std::string backend;
WindowCfg() WindowCfg()
: title("Extra2D"), w(1280), h(720), vsync(true), priority(0), : title("Extra2D"), w(1280), h(720), vsync(true), priority(0) {}
backend("sdl2") {}
}; };
/** /**
@ -52,12 +50,6 @@ public:
*/ */
IWindow *win() const { return win_.get(); } IWindow *win() const { return win_.get(); }
/**
* @brief
* @return
*/
const std::string &getWindowBackend() const { return cfg_.backend; }
private: private:
WindowCfg cfg_; WindowCfg cfg_;
UniquePtr<IWindow> win_; UniquePtr<IWindow> win_;

View File

@ -1,141 +0,0 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/backend_factory.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
namespace graphics {
std::unordered_map<std::string, BackendFactory::BackendEntry> &
BackendFactory::registry() {
static std::unordered_map<std::string, BackendEntry> reg;
return reg;
}
void BackendFactory::reg(const std::string &name, BackendFn backend,
const std::vector<std::string> &windowBackends) {
registry()[name] = {backend, windowBackends};
E2D_LOG_DEBUG("已注册图形后端: {} (窗口后端数量: {})", name,
windowBackends.size());
}
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("正在创建图形后端: {}", name);
return it->second.createFn();
}
E2D_LOG_ERROR("未找到图形后端 '{}'", name);
return nullptr;
}
UniquePtr<RenderBackend> BackendFactory::createDefaultBackend() {
std::string recommended = getRecommendedBackend();
if (recommended.empty()) {
E2D_LOG_ERROR("无可用的图形后端");
return nullptr;
}
return createBackend(recommended);
}
UniquePtr<RenderBackend>
BackendFactory::createBackendForWindow(const std::string &windowBackend) {
std::string recommended = getRecommendedBackendForWindow(windowBackend);
if (recommended.empty()) {
E2D_LOG_ERROR("未找到与窗口后端 '{}' 兼容的图形后端", windowBackend);
return nullptr;
}
return createBackend(recommended);
}
std::vector<std::string> BackendFactory::backends() {
std::vector<std::string> result;
for (const auto &pair : registry()) {
result.push_back(pair.first);
}
return result;
}
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("未注册任何图形后端");
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("未找到与窗口后端 '{}' 兼容的图形后端", 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;
}
for (const auto &wb : windowBackends) {
if (wb == windowBackend) {
return true;
}
}
return false;
}
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;
}
return {};
}
} // namespace graphics
} // namespace extra2d

View File

@ -1,39 +0,0 @@
#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

View File

@ -1,39 +0,0 @@
#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

View File

@ -1,150 +0,0 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/vulkan/vk_renderer.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
VulkanRenderer::VulkanRenderer() = default;
VulkanRenderer::~VulkanRenderer() { shutdown(); }
bool VulkanRenderer::init(IWindow *window) {
E2D_LOG_WARN("Vulkan 渲染器尚未完全实现");
initialized_ = true;
return true;
}
void VulkanRenderer::shutdown() { initialized_ = false; }
void VulkanRenderer::beginFrame(const Color &clearColor) {
// TODO: 实现Vulkan帧开始
}
void VulkanRenderer::endFrame() {
// TODO: 实现Vulkan帧结束
}
void VulkanRenderer::setViewport(int x, int y, int width, int height) {
// TODO: 实现视口设置
}
void VulkanRenderer::setVSync(bool enabled) {
// TODO: 实现垂直同步设置
}
void VulkanRenderer::setBlendMode(BlendMode mode) {
// TODO: 实现混合模式设置
}
void VulkanRenderer::setViewProjection(const glm::mat4 &matrix) {
// TODO: 实现视图投影矩阵设置
}
void VulkanRenderer::pushTransform(const glm::mat4 &transform) {
// TODO: 实现变换矩阵入栈
}
void VulkanRenderer::popTransform() {
// TODO: 实现变换矩阵出栈
}
glm::mat4 VulkanRenderer::getCurrentTransform() const {
return glm::mat4(1.0f);
}
Ptr<Texture> VulkanRenderer::createTexture(int width, int height,
const uint8_t *pixels,
int channels) {
// TODO: 实现Vulkan纹理创建
return nullptr;
}
Ptr<Texture> VulkanRenderer::loadTexture(const std::string &filepath) {
// TODO: 实现Vulkan纹理加载
return nullptr;
}
void VulkanRenderer::beginSpriteBatch() {
// TODO: 实现精灵批处理开始
}
void VulkanRenderer::drawSprite(const Texture &texture, const Rect &destRect,
const Rect &srcRect, const Color &tint,
float rotation, const Vec2 &anchor) {
// TODO: 实现精灵绘制
}
void VulkanRenderer::drawSprite(const Texture &texture, const Vec2 &position,
const Color &tint) {
// TODO: 实现简化精灵绘制
}
void VulkanRenderer::endSpriteBatch() {
// TODO: 实现精灵批处理结束
}
void VulkanRenderer::drawLine(const Vec2 &start, const Vec2 &end,
const Color &color, float width) {
// TODO: 实现线条绘制
}
void VulkanRenderer::drawRect(const Rect &rect, const Color &color,
float width) {
// TODO: 实现矩形边框绘制
}
void VulkanRenderer::fillRect(const Rect &rect, const Color &color) {
// TODO: 实现矩形填充
}
void VulkanRenderer::drawCircle(const Vec2 &center, float radius,
const Color &color, int segments, float width) {
// TODO: 实现圆形边框绘制
}
void VulkanRenderer::fillCircle(const Vec2 &center, float radius,
const Color &color, int segments) {
// TODO: 实现圆形填充
}
void VulkanRenderer::drawTriangle(const Vec2 &p1, const Vec2 &p2,
const Vec2 &p3, const Color &color,
float width) {
// TODO: 实现三角形边框绘制
}
void VulkanRenderer::fillTriangle(const Vec2 &p1, const Vec2 &p2,
const Vec2 &p3, const Color &color) {
// TODO: 实现三角形填充
}
void VulkanRenderer::drawPolygon(const std::vector<Vec2> &points,
const Color &color, float width) {
// TODO: 实现多边形边框绘制
}
void VulkanRenderer::fillPolygon(const std::vector<Vec2> &points,
const Color &color) {
// TODO: 实现多边形填充
}
Ptr<FontAtlas> VulkanRenderer::createFontAtlas(const std::string &filepath,
int fontSize, bool useSDF) {
// TODO: 实现字体图集创建
return nullptr;
}
void VulkanRenderer::drawText(const FontAtlas &font, const std::string &text,
const Vec2 &position, const Color &color) {
// TODO: 实现文本绘制
}
void VulkanRenderer::drawText(const FontAtlas &font, const std::string &text,
float x, float y, const Color &color) {
// TODO: 实现文本绘制
}
void VulkanRenderer::resetStats() { stats_ = Stats{}; }
} // namespace extra2d

View File

@ -1,7 +1,7 @@
#include <extra2d/app/application.h> #include <extra2d/app/application.h>
#include <extra2d/core/registry.h> #include <extra2d/core/registry.h>
#include <extra2d/core/service_locator.h> #include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/backend_factory.h> #include <extra2d/graphics/backends/opengl/gl_renderer.h>
#include <extra2d/graphics/backends/opengl/gl_shader.h> #include <extra2d/graphics/backends/opengl/gl_shader.h>
#include <extra2d/graphics/core/render_module.h> #include <extra2d/graphics/core/render_module.h>
#include <extra2d/graphics/shader/shader_manager.h> #include <extra2d/graphics/shader/shader_manager.h>
@ -10,16 +10,6 @@
namespace extra2d { namespace extra2d {
// 前向声明后端初始化函数
namespace graphics {
#ifdef E2D_BACKEND_OPENGL
void initOpenGLBackend();
#endif
#ifdef E2D_BACKEND_VULKAN
void initVulkanBackend();
#endif
} // namespace graphics
RenderModule::RenderModule(std::function<void(RenderCfg &)> configFn) { RenderModule::RenderModule(std::function<void(RenderCfg &)> configFn) {
configFn(cfg_); configFn(cfg_);
} }
@ -40,15 +30,6 @@ bool RenderModule::init() {
return false; return false;
} }
// 初始化图形后端(注册到工厂)
#ifdef E2D_BACKEND_OPENGL
graphics::initOpenGLBackend();
#endif
#ifdef E2D_BACKEND_VULKAN
graphics::initVulkanBackend();
#endif
// 使用ShaderManager的默认路径初始化
if (!ShaderManager::getInstance().isInitialized()) { if (!ShaderManager::getInstance().isInitialized()) {
auto factory = makeShared<GLShaderFactory>(); auto factory = makeShared<GLShaderFactory>();
if (!ShaderManager::getInstance().init(factory)) { if (!ShaderManager::getInstance().init(factory)) {
@ -56,18 +37,8 @@ bool RenderModule::init() {
} }
} }
std::string windowBackend = winMod->getWindowBackend(); E2D_LOG_INFO("正在创建 OpenGL 渲染后端");
renderer_ = makeUnique<GLRenderer>();
if (cfg_.backend.empty()) {
E2D_LOG_INFO("未指定图形后端,正在为窗口后端自动选择:{}", windowBackend);
renderer_ = graphics::BackendFactory::createBackendForWindow(windowBackend);
} else {
if (!graphics::BackendFactory::isCompatible(cfg_.backend, windowBackend)) {
E2D_LOG_WARN("图形后端 '{}' 与窗口后端 '{}' 不兼容", cfg_.backend,
windowBackend);
}
renderer_ = graphics::BackendFactory::createBackend(cfg_.backend);
}
if (!renderer_) { if (!renderer_) {
E2D_LOG_ERROR("创建渲染后端失败"); E2D_LOG_ERROR("创建渲染后端失败");

View File

@ -1,46 +0,0 @@
#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;
return reg;
}
void BackendFactory::reg(const std::string& name, WindowFn win, InputFn in) {
registry()[name] = {win, in};
}
UniquePtr<IWindow> BackendFactory::createWindow(const std::string& name) {
auto& reg = registry();
auto it = reg.find(name);
if (it != reg.end() && it->second.windowFn) {
return it->second.windowFn();
}
return nullptr;
}
UniquePtr<IInput> BackendFactory::createInput(const std::string& name) {
auto& reg = registry();
auto it = reg.find(name);
if (it != reg.end() && it->second.inputFn) {
return it->second.inputFn();
}
return nullptr;
}
std::vector<std::string> BackendFactory::backends() {
std::vector<std::string> result;
for (const auto& pair : registry()) {
result.push_back(pair.first);
}
return result;
}
bool BackendFactory::has(const std::string& name) {
return registry().find(name) != registry().end();
}
} // namespace platform
} // namespace extra2d

View File

@ -1,30 +0,0 @@
#include "glfw_window.h"
#include "glfw_input.h"
#include <extra2d/platform/backend_factory.h>
namespace extra2d {
namespace platform {
namespace {
static bool s_glfwBackendRegistered = false;
}
void initGLFWBackend() {
if (s_glfwBackendRegistered) {
return;
}
s_glfwBackendRegistered = true;
BackendFactory::reg(
"glfw",
[]() -> UniquePtr<IWindow> {
return makeUnique<GLFWWindow>();
},
[]() -> UniquePtr<IInput> {
return makeUnique<GLFWInput>();
}
);
}
} // namespace platform
} // namespace extra2d

View File

@ -1,30 +0,0 @@
#include "sdl2_window.h"
#include "sdl2_input.h"
#include <extra2d/platform/backend_factory.h>
namespace extra2d {
namespace platform {
namespace {
static bool s_sdl2BackendRegistered = false;
}
void initSDL2Backend() {
if (s_sdl2BackendRegistered) {
return;
}
s_sdl2BackendRegistered = true;
BackendFactory::reg(
"sdl2",
[]() -> UniquePtr<IWindow> {
return makeUnique<SDL2Window>();
},
[]() -> UniquePtr<IInput> {
return makeUnique<SDL2Input>();
}
);
}
} // namespace platform
} // namespace extra2d

View File

@ -1,454 +0,0 @@
#include "sdl2_input.h"
#include <cmath>
#include <extra2d/core/service_locator.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
SDL2Input::SDL2Input() {
keyCurrent_.fill(false);
keyPrevious_.fill(false);
mouseCurrent_.fill(false);
mousePrevious_.fill(false);
gamepadCurrent_.fill(false);
gamepadPrevious_.fill(false);
}
SDL2Input::~SDL2Input() { shutdown(); }
void SDL2Input::init() {
E2D_LOG_INFO("SDL2Input 已初始化");
if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0) {
E2D_LOG_WARN("初始化游戏手柄子系统失败: {}", SDL_GetError());
}
openGamepad();
}
void SDL2Input::shutdown() {
closeGamepad();
E2D_LOG_INFO("SDL2Input 已关闭");
}
void SDL2Input::update() {
keyPrevious_ = keyCurrent_;
mousePrevious_ = mouseCurrent_;
gamepadPrevious_ = gamepadCurrent_;
scrollDelta_ = 0.0f;
mouseDelta_ = Vec2{0.0f, 0.0f};
updateGamepad();
}
void SDL2Input::setEventCallback(EventCallback callback) {
eventCallback_ = std::move(callback);
}
void SDL2Input::handleSDLEvent(const SDL_Event &event) {
switch (event.type) {
case SDL_KEYDOWN: {
int key = event.key.keysym.scancode;
if (key >= 0 && key < static_cast<int>(Key::Count)) {
if (!keyCurrent_[key]) {
keyCurrent_[key] = true;
Event e = Event::createKeyPress(event.key.keysym.sym,
event.key.keysym.scancode,
event.key.keysym.mod);
dispatchEvent(e);
}
}
break;
}
case SDL_KEYUP: {
int key = event.key.keysym.scancode;
if (key >= 0 && key < static_cast<int>(Key::Count)) {
keyCurrent_[key] = false;
Event e = Event::createKeyRelease(event.key.keysym.sym,
event.key.keysym.scancode,
event.key.keysym.mod);
dispatchEvent(e);
}
break;
}
case SDL_MOUSEBUTTONDOWN: {
int btn = event.button.button - 1;
if (btn >= 0 && btn < static_cast<int>(Mouse::Count)) {
mouseCurrent_[btn] = true;
Vec2 pos{static_cast<float>(event.button.x),
static_cast<float>(event.button.y)};
Event e = Event::createMouseButtonPress(btn, 0, pos);
dispatchEvent(e);
}
break;
}
case SDL_MOUSEBUTTONUP: {
int btn = event.button.button - 1;
if (btn >= 0 && btn < static_cast<int>(Mouse::Count)) {
mouseCurrent_[btn] = false;
Vec2 pos{static_cast<float>(event.button.x),
static_cast<float>(event.button.y)};
Event e = Event::createMouseButtonRelease(btn, 0, pos);
dispatchEvent(e);
}
break;
}
case SDL_MOUSEMOTION: {
Vec2 newPos{static_cast<float>(event.motion.x),
static_cast<float>(event.motion.y)};
Vec2 delta{static_cast<float>(event.motion.xrel),
static_cast<float>(event.motion.yrel)};
mouseDelta_ = mouseDelta_ + delta;
mousePos_ = newPos;
Event e = Event::createMouseMove(newPos, delta);
dispatchEvent(e);
break;
}
case SDL_MOUSEWHEEL: {
Vec2 offset{static_cast<float>(event.wheel.x),
static_cast<float>(event.wheel.y)};
Vec2 pos = mousePos_;
scroll_ += event.wheel.y;
scrollDelta_ += event.wheel.y;
Event e = Event::createMouseScroll(offset, pos);
dispatchEvent(e);
break;
}
case SDL_CONTROLLERDEVICEADDED:
E2D_LOG_INFO("游戏手柄已连接: 索引 {}", event.cdevice.which);
openGamepad();
break;
case SDL_CONTROLLERDEVICEREMOVED:
if (gamepad_ &&
event.cdevice.which ==
SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamepad_))) {
E2D_LOG_INFO("游戏手柄已断开");
closeGamepad();
}
break;
case SDL_CONTROLLERBUTTONDOWN:
if (gamepad_) {
int btn = event.cbutton.button;
if (btn >= 0 && btn < static_cast<int>(Gamepad::Count)) {
gamepadCurrent_[btn] = true;
GamepadButtonEvent btnEvent;
btnEvent.gamepadId = gamepadIndex_;
btnEvent.button = btn;
Event e;
e.type = EventType::GamepadButtonPressed;
e.data = btnEvent;
dispatchEvent(e);
}
}
break;
case SDL_CONTROLLERBUTTONUP:
if (gamepad_) {
int btn = event.cbutton.button;
if (btn >= 0 && btn < static_cast<int>(Gamepad::Count)) {
gamepadCurrent_[btn] = false;
GamepadButtonEvent btnEvent;
btnEvent.gamepadId = gamepadIndex_;
btnEvent.button = btn;
Event e;
e.type = EventType::GamepadButtonReleased;
e.data = btnEvent;
dispatchEvent(e);
}
}
break;
default:
break;
}
}
void SDL2Input::dispatchEvent(const Event &event) {
if (eventCallback_) {
eventCallback_(event);
}
}
bool SDL2Input::down(Key key) const {
size_t idx = static_cast<size_t>(key);
if (idx < keyCurrent_.size()) {
return keyCurrent_[idx];
}
return false;
}
bool SDL2Input::pressed(Key key) const {
size_t idx = static_cast<size_t>(key);
if (idx < keyCurrent_.size()) {
return keyCurrent_[idx] && !keyPrevious_[idx];
}
return false;
}
bool SDL2Input::released(Key key) const {
size_t idx = static_cast<size_t>(key);
if (idx < keyCurrent_.size()) {
return !keyCurrent_[idx] && keyPrevious_[idx];
}
return false;
}
bool SDL2Input::down(Mouse btn) const {
size_t idx = static_cast<size_t>(btn);
if (idx < mouseCurrent_.size()) {
return mouseCurrent_[idx];
}
return false;
}
bool SDL2Input::pressed(Mouse btn) const {
size_t idx = static_cast<size_t>(btn);
if (idx < mouseCurrent_.size()) {
return mouseCurrent_[idx] && !mousePrevious_[idx];
}
return false;
}
bool SDL2Input::released(Mouse btn) const {
size_t idx = static_cast<size_t>(btn);
if (idx < mouseCurrent_.size()) {
return !mouseCurrent_[idx] && mousePrevious_[idx];
}
return false;
}
Vec2 SDL2Input::mouse() const { return mousePos_; }
Vec2 SDL2Input::mouseDelta() const { return mouseDelta_; }
float SDL2Input::scroll() const { return scroll_; }
float SDL2Input::scrollDelta() const { return scrollDelta_; }
void SDL2Input::setMouse(const Vec2 &pos) {
SDL_WarpMouseInWindow(nullptr, static_cast<int>(pos.x),
static_cast<int>(pos.y));
}
bool SDL2Input::gamepad() const { return gamepad_ != nullptr; }
bool SDL2Input::down(Gamepad btn) const {
size_t idx = static_cast<size_t>(btn);
if (idx < gamepadCurrent_.size()) {
return gamepadCurrent_[idx];
}
return false;
}
bool SDL2Input::pressed(Gamepad btn) const {
size_t idx = static_cast<size_t>(btn);
if (idx < gamepadCurrent_.size()) {
return gamepadCurrent_[idx] && !gamepadPrevious_[idx];
}
return false;
}
bool SDL2Input::released(Gamepad btn) const {
size_t idx = static_cast<size_t>(btn);
if (idx < gamepadCurrent_.size()) {
return !gamepadCurrent_[idx] && gamepadPrevious_[idx];
}
return false;
}
Vec2 SDL2Input::leftStick() const { return leftStick_; }
Vec2 SDL2Input::rightStick() const { return rightStick_; }
float SDL2Input::leftTrigger() const { return leftTrigger_; }
float SDL2Input::rightTrigger() const { return rightTrigger_; }
void SDL2Input::vibrate(float left, float right) {
if (gamepad_) {
Uint16 lowFreq = static_cast<Uint16>(left * 65535.0f);
Uint16 highFreq = static_cast<Uint16>(right * 65535.0f);
SDL_GameControllerRumble(gamepad_, lowFreq, highFreq, 500);
}
}
bool SDL2Input::touching() const { return false; }
int SDL2Input::touchCount() const { return 0; }
Vec2 SDL2Input::touch(int index) const {
(void)index;
return Vec2{0.0f, 0.0f};
}
TouchPoint SDL2Input::touchPoint(int index) const {
(void)index;
return TouchPoint{};
}
void SDL2Input::updateKeyboard() {}
void SDL2Input::updateMouse() {
int x = 0, y = 0;
SDL_GetMouseState(&x, &y);
mousePos_ = Vec2{static_cast<float>(x), static_cast<float>(y)};
}
void SDL2Input::updateGamepad() {
if (!gamepad_) {
return;
}
auto applyDeadzone = [this](float value) -> float {
if (std::abs(value) < deadzone_) {
return 0.0f;
}
float sign = value >= 0.0f ? 1.0f : -1.0f;
return sign * (std::abs(value) - deadzone_) / (1.0f - deadzone_);
};
int lx = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_LEFTX);
int ly = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_LEFTY);
int rx = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_RIGHTX);
int ry = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_RIGHTY);
leftStick_.x = applyDeadzone(lx / 32767.0f);
leftStick_.y = applyDeadzone(ly / 32767.0f);
rightStick_.x = applyDeadzone(rx / 32767.0f);
rightStick_.y = applyDeadzone(ry / 32767.0f);
int lt = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
int rt =
SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
leftTrigger_ = lt / 32767.0f;
rightTrigger_ = rt / 32767.0f;
}
void SDL2Input::openGamepad() {
int numJoysticks = SDL_NumJoysticks();
for (int i = 0; i < numJoysticks; ++i) {
if (SDL_IsGameController(i)) {
gamepad_ = SDL_GameControllerOpen(i);
if (gamepad_) {
gamepadIndex_ = i;
E2D_LOG_INFO("游戏手柄已打开: {}", SDL_GameControllerName(gamepad_));
return;
}
}
}
}
void SDL2Input::closeGamepad() {
if (gamepad_) {
SDL_GameControllerClose(gamepad_);
gamepad_ = nullptr;
gamepadIndex_ = -1;
gamepadCurrent_.fill(false);
gamepadPrevious_.fill(false);
}
}
int SDL2Input::keyToSDL(Key key) { return static_cast<int>(key); }
int SDL2Input::mouseToSDL(Mouse btn) {
switch (btn) {
case Mouse::Left:
return SDL_BUTTON_LEFT;
case Mouse::Middle:
return SDL_BUTTON_MIDDLE;
case Mouse::Right:
return SDL_BUTTON_RIGHT;
case Mouse::X1:
return SDL_BUTTON_X1;
case Mouse::X2:
return SDL_BUTTON_X2;
default:
return 0;
}
}
int SDL2Input::gamepadToSDL(Gamepad btn) {
switch (btn) {
case Gamepad::A:
return SDL_CONTROLLER_BUTTON_A;
case Gamepad::B:
return SDL_CONTROLLER_BUTTON_B;
case Gamepad::X:
return SDL_CONTROLLER_BUTTON_X;
case Gamepad::Y:
return SDL_CONTROLLER_BUTTON_Y;
case Gamepad::Back:
return SDL_CONTROLLER_BUTTON_BACK;
case Gamepad::Start:
return SDL_CONTROLLER_BUTTON_START;
case Gamepad::LStick:
return SDL_CONTROLLER_BUTTON_LEFTSTICK;
case Gamepad::RStick:
return SDL_CONTROLLER_BUTTON_RIGHTSTICK;
case Gamepad::LB:
return SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
case Gamepad::RB:
return SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
case Gamepad::DUp:
return SDL_CONTROLLER_BUTTON_DPAD_UP;
case Gamepad::DDown:
return SDL_CONTROLLER_BUTTON_DPAD_DOWN;
case Gamepad::DLeft:
return SDL_CONTROLLER_BUTTON_DPAD_LEFT;
case Gamepad::DRight:
return SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
case Gamepad::Guide:
return SDL_CONTROLLER_BUTTON_GUIDE;
default:
return 0;
}
}
Key SDL2Input::sdlToKey(int sdlKey) {
if (sdlKey >= 0 && sdlKey < static_cast<int>(Key::Count)) {
return static_cast<Key>(sdlKey);
}
return Key::None;
}
Mouse SDL2Input::sdlToMouse(int sdlButton) {
switch (sdlButton) {
case SDL_BUTTON_LEFT:
return Mouse::Left;
case SDL_BUTTON_MIDDLE:
return Mouse::Middle;
case SDL_BUTTON_RIGHT:
return Mouse::Right;
case SDL_BUTTON_X1:
return Mouse::X1;
case SDL_BUTTON_X2:
return Mouse::X2;
default:
return Mouse::Count;
}
}
} // namespace extra2d

View File

@ -1,104 +0,0 @@
#pragma once
#include <extra2d/platform/iinput.h>
#include <extra2d/event/event.h>
#include <SDL.h>
#include <array>
#include <functional>
namespace extra2d {
/**
* @brief SDL2
*/
class SDL2Input : public IInput {
public:
using EventCallback = std::function<void(const Event&)>;
SDL2Input();
~SDL2Input() override;
void init() override;
void shutdown() override;
void update() override;
bool down(Key key) const override;
bool pressed(Key key) const override;
bool released(Key key) const override;
bool down(Mouse btn) const override;
bool pressed(Mouse btn) const override;
bool released(Mouse btn) const override;
Vec2 mouse() const override;
Vec2 mouseDelta() const override;
float scroll() const override;
float scrollDelta() const override;
void setMouse(const Vec2& pos) override;
bool gamepad() const override;
bool down(Gamepad btn) const override;
bool pressed(Gamepad btn) const override;
bool released(Gamepad btn) const override;
Vec2 leftStick() const override;
Vec2 rightStick() const override;
float leftTrigger() const override;
float rightTrigger() const override;
void vibrate(float left, float right) override;
bool touching() const override;
int touchCount() const override;
Vec2 touch(int index) const override;
TouchPoint touchPoint(int index) const override;
/**
* @brief
* @param callback
*/
void setEventCallback(EventCallback callback);
/**
* @brief SDL
* @param event SDL
*/
void handleSDLEvent(const SDL_Event& event);
private:
void updateKeyboard();
void updateMouse();
void updateGamepad();
void openGamepad();
void closeGamepad();
static int keyToSDL(Key key);
static int mouseToSDL(Mouse btn);
static int gamepadToSDL(Gamepad btn);
static Key sdlToKey(int sdlKey);
static Mouse sdlToMouse(int sdlButton);
void dispatchEvent(const Event& event);
std::array<bool, static_cast<size_t>(Key::Count)> keyCurrent_{};
std::array<bool, static_cast<size_t>(Key::Count)> keyPrevious_{};
std::array<bool, static_cast<size_t>(Mouse::Count)> mouseCurrent_{};
std::array<bool, static_cast<size_t>(Mouse::Count)> mousePrevious_{};
Vec2 mousePos_;
Vec2 mouseDelta_;
float scroll_ = 0.0f;
float scrollDelta_ = 0.0f;
SDL_GameController* gamepad_ = nullptr;
int gamepadIndex_ = -1;
std::array<bool, static_cast<size_t>(Gamepad::Count)> gamepadCurrent_{};
std::array<bool, static_cast<size_t>(Gamepad::Count)> gamepadPrevious_{};
Vec2 leftStick_;
Vec2 rightStick_;
float leftTrigger_ = 0.0f;
float rightTrigger_ = 0.0f;
float deadzone_ = 0.15f;
EventCallback eventCallback_;
};
}

View File

@ -1,615 +0,0 @@
#include "sdl2_window.h"
#include "sdl2_input.h"
#include <extra2d/core/service_locator.h>
#include <extra2d/event/event.h>
#include <extra2d/platform/keys.h>
#include <extra2d/services/event_service.h>
#include <extra2d/services/logger_service.h>
#include <glad/glad.h>
namespace extra2d {
SDL2Window::SDL2Window() {
for (int i = 0; i < 7; ++i) {
sdlCursors_[i] = nullptr;
}
}
SDL2Window::~SDL2Window() { destroy(); }
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 | SDL_WINDOW_RESIZABLE;
#ifdef __SWITCH__
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
#endif
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
sdlWindow_ = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, width, height, flags);
if (!sdlWindow_) {
E2D_LOG_ERROR("创建 SDL 窗口失败: {}", SDL_GetError());
deinitSDL();
return false;
}
glContext_ = SDL_GL_CreateContext(sdlWindow_);
if (!glContext_) {
E2D_LOG_ERROR("创建 OpenGL 上下文失败: {}", SDL_GetError());
SDL_DestroyWindow(sdlWindow_);
sdlWindow_ = nullptr;
deinitSDL();
return false;
}
if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) {
E2D_LOG_ERROR("初始化 GLAD GLES2 失败");
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
SDL_DestroyWindow(sdlWindow_);
sdlWindow_ = nullptr;
deinitSDL();
return false;
}
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
SDL_GetWindowSize(sdlWindow_, &width_, &height_);
fullscreen_ = (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
vsync_ = vsync;
#ifndef __SWITCH__
initCursors();
#endif
updateContentScale();
input_ = makeUnique<SDL2Input>();
input_->init();
E2D_LOG_INFO("SDL2 窗口创建成功: {}x{}", width_, height_);
E2D_LOG_INFO(" 平台: OpenGL ES 3.2");
return true;
}
void SDL2Window::destroy() {
if (input_) {
input_->shutdown();
input_.reset();
}
#ifndef __SWITCH__
deinitCursors();
#endif
if (glContext_) {
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
}
if (sdlWindow_) {
SDL_DestroyWindow(sdlWindow_);
sdlWindow_ = nullptr;
}
deinitSDL();
}
void SDL2Window::poll() {
if (!sdlWindow_)
return;
if (input_) {
input_->update();
}
SDL_Event event;
while (SDL_PollEvent(&event)) {
handleEvent(event);
}
}
void SDL2Window::swap() {
if (sdlWindow_ && glContext_) {
SDL_GL_SwapWindow(sdlWindow_);
}
}
bool SDL2Window::shouldClose() const { return shouldClose_; }
void SDL2Window::close() { shouldClose_ = true; }
void SDL2Window::setTitle(const std::string &title) {
if (sdlWindow_) {
SDL_SetWindowTitle(sdlWindow_, title.c_str());
}
}
void SDL2Window::setSize(int w, int h) {
if (sdlWindow_) {
SDL_SetWindowSize(sdlWindow_, w, h);
width_ = w;
height_ = h;
}
}
void SDL2Window::setPos(int x, int y) {
#ifndef __SWITCH__
if (sdlWindow_) {
SDL_SetWindowPosition(sdlWindow_, x, y);
}
#else
(void)x;
(void)y;
#endif
}
void SDL2Window::setFullscreen(bool fs) {
#ifndef __SWITCH__
if (sdlWindow_) {
SDL_SetWindowFullscreen(sdlWindow_, fs ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
fullscreen_ = fs;
}
#else
(void)fs;
#endif
}
void SDL2Window::setVSync(bool vsync) {
if (glContext_) {
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
vsync_ = vsync;
}
}
void SDL2Window::setVisible(bool visible) {
#ifndef __SWITCH__
if (sdlWindow_) {
if (visible) {
SDL_ShowWindow(sdlWindow_);
} else {
SDL_HideWindow(sdlWindow_);
}
}
#else
(void)visible;
#endif
}
int SDL2Window::width() const { return width_; }
int SDL2Window::height() const { return height_; }
Size SDL2Window::size() const {
return Size(static_cast<float>(width_), static_cast<float>(height_));
}
Vec2 SDL2Window::pos() const {
int x = 0, y = 0;
#ifndef __SWITCH__
if (sdlWindow_) {
SDL_GetWindowPosition(sdlWindow_, &x, &y);
}
#endif
return Vec2(static_cast<float>(x), static_cast<float>(y));
}
bool SDL2Window::fullscreen() const { return fullscreen_; }
bool SDL2Window::vsync() const { return vsync_; }
bool SDL2Window::focused() const { return focused_; }
bool SDL2Window::minimized() const { return minimized_; }
float SDL2Window::scaleX() const { return scaleX_; }
float SDL2Window::scaleY() const { return scaleY_; }
void SDL2Window::setCursor(Cursor cursor) {
#ifndef __SWITCH__
if (cursor == Cursor::Hidden) {
SDL_ShowCursor(SDL_DISABLE);
return;
}
SDL_ShowCursor(SDL_ENABLE);
int idx = static_cast<int>(cursor);
if (idx >= 0 && idx < 7 && sdlCursors_[idx]) {
SDL_SetCursor(sdlCursors_[idx]);
currentCursor_ = idx;
}
#else
(void)cursor;
#endif
}
void SDL2Window::showCursor(bool show) {
#ifndef __SWITCH__
SDL_ShowCursor(show ? SDL_ENABLE : SDL_DISABLE);
cursorVisible_ = show;
#else
(void)show;
#endif
}
void SDL2Window::lockCursor(bool lock) {
#ifndef __SWITCH__
if (sdlWindow_) {
SDL_SetRelativeMouseMode(lock ? SDL_TRUE : SDL_FALSE);
cursorLocked_ = lock;
}
#else
(void)lock;
#endif
}
IInput *SDL2Window::input() const { return input_.get(); }
void SDL2Window::onResize(ResizeCb cb) { resizeCb_ = cb; }
void SDL2Window::onClose(CloseCb cb) { closeCb_ = cb; }
void SDL2Window::onFocus(FocusCb cb) { focusCb_ = cb; }
void *SDL2Window::native() const { return sdlWindow_; }
bool SDL2Window::initSDL() {
static int sdlInitCount = 0;
if (sdlInitCount == 0) {
Uint32 initFlags = SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER;
if (SDL_Init(initFlags) != 0) {
E2D_LOG_ERROR("初始化 SDL 失败: {}", SDL_GetError());
return false;
}
sdlInitCount++;
}
return true;
}
void SDL2Window::deinitSDL() {
static int sdlInitCount = 1;
sdlInitCount--;
if (sdlInitCount == 0) {
SDL_Quit();
}
}
#ifndef __SWITCH__
void SDL2Window::initCursors() {
sdlCursors_[0] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
sdlCursors_[1] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
sdlCursors_[2] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR);
sdlCursors_[3] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
sdlCursors_[4] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
sdlCursors_[5] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
sdlCursors_[6] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
}
void SDL2Window::deinitCursors() {
for (int i = 0; i < 7; ++i) {
if (sdlCursors_[i]) {
SDL_FreeCursor(sdlCursors_[i]);
sdlCursors_[i] = nullptr;
}
}
}
#endif
void SDL2Window::updateContentScale() {
if (sdlWindow_) {
SDL_GetWindowSize(sdlWindow_, &width_, &height_);
int dw, dh;
SDL_GL_GetDrawableSize(sdlWindow_, &dw, &dh);
scaleX_ = dw > 0 ? static_cast<float>(dw) / width_ : 1.0f;
scaleY_ = dh > 0 ? static_cast<float>(dh) / height_ : 1.0f;
}
}
void SDL2Window::handleEvent(const SDL_Event &event) {
if (input_) {
input_->handleSDLEvent(event);
}
// 将事件推送到事件服务
auto eventService = ServiceLocator::instance().getService<IEventService>();
switch (event.type) {
case SDL_QUIT:
shouldClose_ = true;
if (closeCb_)
closeCb_();
if (eventService) {
Event e = Event::createWindowClose();
eventService->pushEvent(e);
}
break;
case SDL_WINDOWEVENT:
switch (event.window.event) {
case SDL_WINDOWEVENT_RESIZED:
case SDL_WINDOWEVENT_SIZE_CHANGED:
width_ = event.window.data1;
height_ = event.window.data2;
updateContentScale();
if (resizeCb_)
resizeCb_(width_, height_);
if (eventService) {
Event e = Event::createWindowResize(width_, height_);
eventService->pushEvent(e);
}
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
focused_ = true;
if (focusCb_)
focusCb_(true);
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
focused_ = false;
if (focusCb_)
focusCb_(false);
break;
case SDL_WINDOWEVENT_MINIMIZED:
minimized_ = true;
break;
case SDL_WINDOWEVENT_RESTORED:
minimized_ = false;
break;
case SDL_WINDOWEVENT_CLOSE:
shouldClose_ = true;
if (closeCb_)
closeCb_();
if (eventService) {
Event e = Event::createWindowClose();
eventService->pushEvent(e);
}
break;
}
break;
case SDL_KEYDOWN: {
if (event.key.repeat == 0 && eventService) {
// 将 SDL scancode 转换为 Key 枚举
Key key = sdlScancodeToKey(event.key.keysym.scancode);
if (key != Key::None) {
int keyCode = static_cast<int>(key);
Event e = Event::createKeyPress(keyCode, event.key.keysym.scancode,
event.key.keysym.mod);
eventService->pushEvent(e);
}
}
break;
}
case SDL_KEYUP: {
if (eventService) {
Key key = sdlScancodeToKey(event.key.keysym.scancode);
if (key != Key::None) {
int keyCode = static_cast<int>(key);
Event e = Event::createKeyRelease(keyCode, event.key.keysym.scancode,
event.key.keysym.mod);
eventService->pushEvent(e);
}
}
break;
}
case SDL_MOUSEBUTTONDOWN: {
if (eventService) {
Vec2 pos{static_cast<float>(event.button.x),
static_cast<float>(event.button.y)};
Event e = Event::createMouseButtonPress(event.button.button - 1, 0, pos);
eventService->pushEvent(e);
}
break;
}
case SDL_MOUSEBUTTONUP: {
if (eventService) {
Vec2 pos{static_cast<float>(event.button.x),
static_cast<float>(event.button.y)};
Event e =
Event::createMouseButtonRelease(event.button.button - 1, 0, pos);
eventService->pushEvent(e);
}
break;
}
case SDL_MOUSEMOTION: {
if (eventService) {
Vec2 pos{static_cast<float>(event.motion.x),
static_cast<float>(event.motion.y)};
Vec2 delta{static_cast<float>(event.motion.xrel),
static_cast<float>(event.motion.yrel)};
Event e = Event::createMouseMove(pos, delta);
eventService->pushEvent(e);
}
break;
}
case SDL_MOUSEWHEEL: {
if (eventService) {
int x, y;
SDL_GetMouseState(&x, &y);
Vec2 offset{static_cast<float>(event.wheel.x),
static_cast<float>(event.wheel.y)};
Vec2 pos{static_cast<float>(x), static_cast<float>(y)};
Event e = Event::createMouseScroll(offset, pos);
eventService->pushEvent(e);
}
break;
}
}
}
Key SDL2Window::sdlScancodeToKey(int scancode) {
switch (scancode) {
case SDL_SCANCODE_A:
return Key::A;
case SDL_SCANCODE_B:
return Key::B;
case SDL_SCANCODE_C:
return Key::C;
case SDL_SCANCODE_D:
return Key::D;
case SDL_SCANCODE_E:
return Key::E;
case SDL_SCANCODE_F:
return Key::F;
case SDL_SCANCODE_G:
return Key::G;
case SDL_SCANCODE_H:
return Key::H;
case SDL_SCANCODE_I:
return Key::I;
case SDL_SCANCODE_J:
return Key::J;
case SDL_SCANCODE_K:
return Key::K;
case SDL_SCANCODE_L:
return Key::L;
case SDL_SCANCODE_M:
return Key::M;
case SDL_SCANCODE_N:
return Key::N;
case SDL_SCANCODE_O:
return Key::O;
case SDL_SCANCODE_P:
return Key::P;
case SDL_SCANCODE_Q:
return Key::Q;
case SDL_SCANCODE_R:
return Key::R;
case SDL_SCANCODE_S:
return Key::S;
case SDL_SCANCODE_T:
return Key::T;
case SDL_SCANCODE_U:
return Key::U;
case SDL_SCANCODE_V:
return Key::V;
case SDL_SCANCODE_W:
return Key::W;
case SDL_SCANCODE_X:
return Key::X;
case SDL_SCANCODE_Y:
return Key::Y;
case SDL_SCANCODE_Z:
return Key::Z;
case SDL_SCANCODE_0:
return Key::Num0;
case SDL_SCANCODE_1:
return Key::Num1;
case SDL_SCANCODE_2:
return Key::Num2;
case SDL_SCANCODE_3:
return Key::Num3;
case SDL_SCANCODE_4:
return Key::Num4;
case SDL_SCANCODE_5:
return Key::Num5;
case SDL_SCANCODE_6:
return Key::Num6;
case SDL_SCANCODE_7:
return Key::Num7;
case SDL_SCANCODE_8:
return Key::Num8;
case SDL_SCANCODE_9:
return Key::Num9;
case SDL_SCANCODE_F1:
return Key::F1;
case SDL_SCANCODE_F2:
return Key::F2;
case SDL_SCANCODE_F3:
return Key::F3;
case SDL_SCANCODE_F4:
return Key::F4;
case SDL_SCANCODE_F5:
return Key::F5;
case SDL_SCANCODE_F6:
return Key::F6;
case SDL_SCANCODE_F7:
return Key::F7;
case SDL_SCANCODE_F8:
return Key::F8;
case SDL_SCANCODE_F9:
return Key::F9;
case SDL_SCANCODE_F10:
return Key::F10;
case SDL_SCANCODE_F11:
return Key::F11;
case SDL_SCANCODE_F12:
return Key::F12;
case SDL_SCANCODE_SPACE:
return Key::Space;
case SDL_SCANCODE_RETURN:
return Key::Enter;
case SDL_SCANCODE_ESCAPE:
return Key::Escape;
case SDL_SCANCODE_TAB:
return Key::Tab;
case SDL_SCANCODE_BACKSPACE:
return Key::Backspace;
case SDL_SCANCODE_INSERT:
return Key::Insert;
case SDL_SCANCODE_DELETE:
return Key::Delete;
case SDL_SCANCODE_HOME:
return Key::Home;
case SDL_SCANCODE_END:
return Key::End;
case SDL_SCANCODE_PAGEUP:
return Key::PageUp;
case SDL_SCANCODE_PAGEDOWN:
return Key::PageDown;
case SDL_SCANCODE_UP:
return Key::Up;
case SDL_SCANCODE_DOWN:
return Key::Down;
case SDL_SCANCODE_LEFT:
return Key::Left;
case SDL_SCANCODE_RIGHT:
return Key::Right;
case SDL_SCANCODE_LSHIFT:
return Key::LShift;
case SDL_SCANCODE_RSHIFT:
return Key::RShift;
case SDL_SCANCODE_LCTRL:
return Key::LCtrl;
case SDL_SCANCODE_RCTRL:
return Key::RCtrl;
case SDL_SCANCODE_LALT:
return Key::LAlt;
case SDL_SCANCODE_RALT:
return Key::RAlt;
case SDL_SCANCODE_CAPSLOCK:
return Key::CapsLock;
case SDL_SCANCODE_NUMLOCKCLEAR:
return Key::NumLock;
case SDL_SCANCODE_SCROLLLOCK:
return Key::ScrollLock;
default:
return Key::None;
}
}
} // namespace extra2d

View File

@ -1,101 +0,0 @@
#pragma once
#include <extra2d/platform/iwindow.h>
#include <extra2d/platform/keys.h>
#include <SDL.h>
namespace extra2d {
class SDL2Input;
/**
* @brief SDL2
*/
class SDL2Window : public IWindow {
public:
SDL2Window();
~SDL2Window() override;
bool create(const std::string& title, int width, int height, bool vsync = true) override;
void destroy() override;
void poll() override;
void swap() override;
bool shouldClose() const override;
void close() override;
void setTitle(const std::string& title) override;
void setSize(int w, int h) override;
void setPos(int x, int y) override;
void setFullscreen(bool fs) override;
void setVSync(bool vsync) override;
void setVisible(bool visible) override;
int width() const override;
int height() const override;
Size size() const override;
Vec2 pos() const override;
bool fullscreen() const override;
bool vsync() const override;
bool focused() const override;
bool minimized() const override;
float scaleX() const override;
float scaleY() const override;
void setCursor(Cursor cursor) override;
void showCursor(bool show) override;
void lockCursor(bool lock) override;
IInput* input() const override;
void onResize(ResizeCb cb) override;
void onClose(CloseCb cb) override;
void onFocus(FocusCb cb) override;
void* native() const override;
/**
* @brief SDL
*/
SDL_Window* sdlWindow() const { return sdlWindow_; }
/**
* @brief OpenGL
*/
SDL_GLContext glContext() const { return glContext_; }
private:
bool initSDL();
void deinitSDL();
void initCursors();
void deinitCursors();
void updateContentScale();
void handleEvent(const SDL_Event& event);
Key sdlScancodeToKey(int scancode);
SDL_Window* sdlWindow_ = nullptr;
SDL_GLContext glContext_ = nullptr;
SDL_Cursor* sdlCursors_[7] = {};
int currentCursor_ = 0;
UniquePtr<SDL2Input> input_;
int width_ = 1280;
int height_ = 720;
bool fullscreen_ = false;
bool vsync_ = true;
bool focused_ = true;
bool minimized_ = false;
bool shouldClose_ = false;
float scaleX_ = 1.0f;
float scaleY_ = 1.0f;
bool cursorVisible_ = true;
bool cursorLocked_ = false;
ResizeCb resizeCb_;
CloseCb closeCb_;
FocusCb focusCb_;
};
} // namespace extra2d

View File

@ -1,23 +1,15 @@
#include <extra2d/core/service_locator.h> #include <extra2d/core/service_locator.h>
#include <extra2d/platform/backend_factory.h>
#include <extra2d/platform/window_module.h> #include <extra2d/platform/window_module.h>
#include <extra2d/services/logger_service.h> #include <extra2d/services/logger_service.h>
#include "backends/glfw/glfw_window.h"
#ifdef __SWITCH__ #ifdef __SWITCH__
#include <switch.h> #include <switch.h>
#endif #endif
namespace extra2d { namespace extra2d {
// 前向声明后端初始化函数
namespace platform {
#if defined(E2D_BACKEND_SDL2)
void initSDL2Backend();
#elif defined(E2D_BACKEND_GLFW)
void initGLFWBackend();
#endif
} // namespace platform
WindowModule::WindowModule(std::function<void(WindowCfg &)> configFn) { WindowModule::WindowModule(std::function<void(WindowCfg &)> configFn) {
configFn(cfg_); configFn(cfg_);
} }
@ -32,23 +24,11 @@ bool WindowModule::init() {
if (initialized_) if (initialized_)
return true; return true;
// 初始化后端(注册到工厂) E2D_LOG_INFO("正在创建 GLFW 窗口,尺寸 {}x{}", cfg_.w, cfg_.h);
#if defined(E2D_BACKEND_SDL2)
platform::initSDL2Backend();
#elif defined(E2D_BACKEND_GLFW)
platform::initGLFWBackend();
#else
#error "No window backend defined"
#endif
E2D_LOG_INFO("窗口后端已初始化"); win_ = makeUnique<GLFWWindow>();
E2D_LOG_INFO("正在创建窗口,尺寸 {}x{}", cfg_.w, cfg_.h);
// 创建窗口(使用配置的后端)
win_ = platform::BackendFactory::createWindow(cfg_.backend);
if (!win_) { if (!win_) {
E2D_LOG_ERROR("创建窗口后端失败: {}", cfg_.backend); E2D_LOG_ERROR("创建窗口失败");
return false; return false;
} }

123
xmake.lua
View File

@ -8,7 +8,8 @@
-- - macOS -- - macOS
-- - Nintendo Switch -- - Nintendo Switch
-- --
-- 窗口后端: SDL2 / GLFW -- 窗口后端: GLFW
-- 渲染后端: OpenGL
-- ============================================== -- ==============================================
-- 项目元信息 -- 项目元信息
@ -33,20 +34,6 @@ option("debug_logs")
set_description("Enable debug logging") set_description("Enable debug logging")
option_end() option_end()
option("window_backend")
set_default("sdl2")
set_showmenu(true)
set_description("Window backend: sdl2 or glfw")
set_values("sdl2", "glfw")
option_end()
option("render_backend")
set_default("opengl")
set_showmenu(true)
set_description("Render backend: opengl or vulkan")
set_values("opengl", "vulkan")
option_end()
-- ============================================== -- ==============================================
-- 平台检测与配置 -- 平台检测与配置
-- ============================================== -- ==============================================
@ -99,25 +86,7 @@ end
if target_plat ~= "switch" then if target_plat ~= "switch" then
add_requires("glm") add_requires("glm")
add_requires("nlohmann_json") add_requires("nlohmann_json")
-- 窗口后端依赖
local backend = get_config("window_backend") or "sdl2"
if backend == "glfw" then
add_requires("glfw") add_requires("glfw")
else
local sdl2_configs = {
configs = {
wayland = false
}
}
if target_plat == "linux" then
local is_wayland = os.getenv("XDG_SESSION_TYPE") == "wayland"
if is_wayland then
sdl2_configs.configs.wayland = true
end
end
add_requires("libsdl2", sdl2_configs)
end
end end
@ -171,30 +140,14 @@ target("demo_basic")
-- 平台配置 -- 平台配置
local plat = get_config("plat") or os.host() local plat = get_config("plat") or os.host()
local backend = get_config("window_backend") or "sdl2"
if plat == "mingw" or plat == "windows" then if plat == "mingw" or plat == "windows" then
add_packages("glm", "nlohmann_json") add_packages("glm", "nlohmann_json", "glfw")
if backend == "glfw" then
add_packages("glfw")
else
add_packages("libsdl2")
end
add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi") add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi")
elseif plat == "linux" then elseif plat == "linux" then
add_packages("glm", "nlohmann_json") add_packages("glm", "nlohmann_json", "glfw")
if backend == "glfw" then
add_packages("glfw")
else
add_packages("libsdl2")
end
add_syslinks("GL", "dl", "pthread") add_syslinks("GL", "dl", "pthread")
elseif plat == "macosx" then elseif plat == "macosx" then
add_packages("glm", "nlohmann_json") add_packages("glm", "nlohmann_json", "glfw")
if backend == "glfw" then
add_packages("glfw")
else
add_packages("libsdl2")
end
add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo") add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo")
end end
@ -212,30 +165,14 @@ target("demo_text_rendering")
-- 平台配置 -- 平台配置
local plat = get_config("plat") or os.host() local plat = get_config("plat") or os.host()
local backend = get_config("window_backend") or "sdl2"
if plat == "mingw" or plat == "windows" then if plat == "mingw" or plat == "windows" then
add_packages("glm", "nlohmann_json") add_packages("glm", "nlohmann_json", "glfw")
if backend == "glfw" then
add_packages("glfw")
else
add_packages("libsdl2")
end
add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi") add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi")
elseif plat == "linux" then elseif plat == "linux" then
add_packages("glm", "nlohmann_json") add_packages("glm", "nlohmann_json", "glfw")
if backend == "glfw" then
add_packages("glfw")
else
add_packages("libsdl2")
end
add_syslinks("GL", "dl", "pthread") add_syslinks("GL", "dl", "pthread")
elseif plat == "macosx" then elseif plat == "macosx" then
add_packages("glm", "nlohmann_json") add_packages("glm", "nlohmann_json", "glfw")
if backend == "glfw" then
add_packages("glfw")
else
add_packages("libsdl2")
end
add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo") add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo")
end end
@ -272,30 +209,14 @@ target("demo_hello_module")
-- 平台配置 -- 平台配置
local plat = get_config("plat") or os.host() local plat = get_config("plat") or os.host()
local backend = get_config("window_backend") or "sdl2"
if plat == "mingw" or plat == "windows" then if plat == "mingw" or plat == "windows" then
add_packages("glm", "nlohmann_json") add_packages("glm", "nlohmann_json", "glfw")
if backend == "glfw" then
add_packages("glfw")
else
add_packages("libsdl2")
end
add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi") add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi")
elseif plat == "linux" then elseif plat == "linux" then
add_packages("glm", "nlohmann_json") add_packages("glm", "nlohmann_json", "glfw")
if backend == "glfw" then
add_packages("glfw")
else
add_packages("libsdl2")
end
add_syslinks("GL", "dl", "pthread") add_syslinks("GL", "dl", "pthread")
elseif plat == "macosx" then elseif plat == "macosx" then
add_packages("glm", "nlohmann_json") add_packages("glm", "nlohmann_json", "glfw")
if backend == "glfw" then
add_packages("glfw")
else
add_packages("libsdl2")
end
add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo") add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo")
end end
@ -313,30 +234,14 @@ target("demo_image_display")
-- 平台配置 -- 平台配置
local plat = get_config("plat") or os.host() local plat = get_config("plat") or os.host()
local backend = get_config("window_backend") or "sdl2"
if plat == "mingw" or plat == "windows" then if plat == "mingw" or plat == "windows" then
add_packages("glm", "nlohmann_json") add_packages("glm", "nlohmann_json", "glfw")
if backend == "glfw" then
add_packages("glfw")
else
add_packages("libsdl2")
end
add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi") add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi")
elseif plat == "linux" then elseif plat == "linux" then
add_packages("glm", "nlohmann_json") add_packages("glm", "nlohmann_json", "glfw")
if backend == "glfw" then
add_packages("glfw")
else
add_packages("libsdl2")
end
add_syslinks("GL", "dl", "pthread") add_syslinks("GL", "dl", "pthread")
elseif plat == "macosx" then elseif plat == "macosx" then
add_packages("glm", "nlohmann_json") add_packages("glm", "nlohmann_json", "glfw")
if backend == "glfw" then
add_packages("glfw")
else
add_packages("libsdl2")
end
add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo") add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo")
end end

View File

@ -2,11 +2,8 @@
-- Extra2D 引擎库共享配置 -- Extra2D 引擎库共享配置
-- 被主项目和示例共享使用 -- 被主项目和示例共享使用
-- --
-- 窗口后端支持 SDL2 和 GLFW -- 窗口后端: GLFW
-- - Windows (MinGW) - 支持 SDL2 和 GLFW -- 渲染后端: OpenGL
-- - Linux - 支持 SDL2 和 GLFW
-- - macOS - 支持 SDL2 和 GLFW
-- - Nintendo Switch - 使用系统提供的后端
-- ============================================== -- ==============================================
-- 获取当前平台 -- 获取当前平台
@ -14,16 +11,6 @@ local function get_current_plat()
return get_config("plat") or os.host() return get_config("plat") or os.host()
end end
-- 获取窗口后端
local function get_window_backend()
return get_config("window_backend") or "sdl2"
end
-- 获取渲染后端
local function get_render_backend()
return get_config("render_backend") or "opengl"
end
-- 定义 Extra2D 引擎库目标 -- 定义 Extra2D 引擎库目标
function define_extra2d_engine() function define_extra2d_engine()
target("extra2d") target("extra2d")
@ -32,28 +19,12 @@ function define_extra2d_engine()
-- 引擎核心源文件(后端无关) -- 引擎核心源文件(后端无关)
add_files("Extra2D/src/**.cpp|platform/backends/**.cpp|graphics/backends/**.cpp") add_files("Extra2D/src/**.cpp|platform/backends/**.cpp|graphics/backends/**.cpp")
-- 渲染后端源文件 -- OpenGL 渲染后端源文件
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")
else
add_files("Extra2D/src/graphics/backends/opengl/*.cpp") add_files("Extra2D/src/graphics/backends/opengl/*.cpp")
add_files("Extra2D/src/glad/glad.c") add_files("Extra2D/src/glad/glad.c")
add_defines("E2D_BACKEND_OPENGL")
end
-- 窗口后端源文件 -- GLFW 窗口后端源文件
local backend = get_window_backend()
if backend == "glfw" then
add_files("Extra2D/src/platform/backends/glfw/*.cpp") add_files("Extra2D/src/platform/backends/glfw/*.cpp")
add_defines("E2D_BACKEND_GLFW")
else
add_files("Extra2D/src/platform/backends/sdl2/*.cpp")
add_defines("E2D_BACKEND_SDL2")
end
-- 头文件路径 -- 头文件路径
add_includedirs("Extra2D/include", {public = true}) add_includedirs("Extra2D/include", {public = true})
@ -68,47 +39,21 @@ function define_extra2d_engine()
add_includedirs(devkitPro .. "/portlibs/switch/include", {public = true}) add_includedirs(devkitPro .. "/portlibs/switch/include", {public = true})
add_linkdirs(devkitPro .. "/portlibs/switch/lib") add_linkdirs(devkitPro .. "/portlibs/switch/lib")
-- Switch 平台根据后端选择链接库 -- GLFW 后端
local backend = get_window_backend()
if backend == "glfw" then
-- GLFW 后端(使用 libnx 提供的 GLFW
add_syslinks("glfw", "GLESv2", "EGL", "glapi", "drm_nouveau", "nx", "m", add_syslinks("glfw", "GLESv2", "EGL", "glapi", "drm_nouveau", "nx", "m",
{public = true}) {public = true})
else
-- SDL2 后端
add_syslinks("SDL2", "GLESv2", "EGL", "glapi", "drm_nouveau", "nx", "m",
{public = true})
end
elseif plat == "mingw" or plat == "windows" then elseif plat == "mingw" or plat == "windows" then
-- Windows (MinGW) 平台配置 -- Windows (MinGW) 平台配置
add_packages("glm", "nlohmann_json", {public = true}) add_packages("glm", "nlohmann_json", "glfw", {public = true})
local backend = get_window_backend()
if backend == "glfw" then
add_packages("glfw", {public = true})
else
add_packages("libsdl2", {public = true})
end
add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi", add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi",
{public = true}) {public = true})
elseif plat == "linux" then elseif plat == "linux" then
-- Linux 平台配置 -- Linux 平台配置
add_packages("glm", "nlohmann_json", {public = true}) add_packages("glm", "nlohmann_json", "glfw", {public = true})
local backend = get_window_backend()
if backend == "glfw" then
add_packages("glfw", {public = true})
else
add_packages("libsdl2", {public = true})
end
add_syslinks("GL", "dl", "pthread", {public = true}) add_syslinks("GL", "dl", "pthread", {public = true})
elseif plat == "macosx" then elseif plat == "macosx" then
-- macOS 平台配置 -- macOS 平台配置
add_packages("glm", "nlohmann_json", {public = true}) add_packages("glm", "nlohmann_json", "glfw", {public = true})
local backend = get_window_backend()
if backend == "glfw" then
add_packages("glfw", {public = true})
else
add_packages("libsdl2", {public = true})
end
add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo", {public = true}) add_frameworks("OpenGL", "Cocoa", "IOKit", "CoreVideo", {public = true})
end end