refactor: 统一方法命名规范并优化代码文档
- 将单例方法名从getInstance统一改为get - 重命名边界框相关方法为getBounds - 重命名位置相关方法为setPos/getPos - 优化代码注释,增加详细文档说明 - 移除调试信息输出 - 统一命名管理器类后缀为Mgr - 重构节点相关方法名,如removeFromParent改为detach - 统一坐标转换方法命名为toWorld/toLocal
This commit is contained in:
parent
c6c90a7374
commit
387ea62853
|
|
@ -36,7 +36,7 @@ struct AppConfig {
|
|||
*/
|
||||
class Application {
|
||||
public:
|
||||
static Application &instance();
|
||||
static Application &get();
|
||||
|
||||
Application(const Application &) = delete;
|
||||
Application &operator=(const Application &) = delete;
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ public:
|
|||
// ------------------------------------------------------------------------
|
||||
// 位置和变换
|
||||
// ------------------------------------------------------------------------
|
||||
void setPosition(const Vec2 &position);
|
||||
void setPosition(float x, float y);
|
||||
void setPos(const Vec2 &position);
|
||||
void setPos(float x, float y);
|
||||
Vec2 getPosition() const { return position_; }
|
||||
|
||||
void setRotation(float degrees);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace extra2d {
|
|||
class GPUContext {
|
||||
public:
|
||||
/// 获取单例实例
|
||||
static GPUContext& getInstance();
|
||||
static GPUContext& get();
|
||||
|
||||
/// 标记 GPU 上下文为有效(在初始化完成后调用)
|
||||
void markValid();
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ private:
|
|||
// ============================================================================
|
||||
class RenderTargetStack {
|
||||
public:
|
||||
static RenderTargetStack &getInstance();
|
||||
static RenderTargetStack &get();
|
||||
|
||||
/**
|
||||
* @brief 压入渲染目标
|
||||
|
|
@ -271,12 +271,12 @@ private:
|
|||
// ============================================================================
|
||||
// 渲染目标管理器 - 全局渲染目标管理
|
||||
// ============================================================================
|
||||
class RenderTargetManager {
|
||||
class RenderTargetMgr {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
*/
|
||||
static RenderTargetManager &getInstance();
|
||||
static RenderTargetMgr& get();
|
||||
|
||||
/**
|
||||
* @brief 初始化渲染目标管理器
|
||||
|
|
@ -313,10 +313,10 @@ public:
|
|||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
private:
|
||||
RenderTargetManager() = default;
|
||||
~RenderTargetManager() = default;
|
||||
RenderTargetManager(const RenderTargetManager &) = delete;
|
||||
RenderTargetManager &operator=(const RenderTargetManager &) = delete;
|
||||
RenderTargetMgr() = default;
|
||||
~RenderTargetMgr() = default;
|
||||
RenderTargetMgr(const RenderTargetMgr &) = delete;
|
||||
RenderTargetMgr &operator=(const RenderTargetMgr &) = delete;
|
||||
|
||||
Ptr<RenderTarget> defaultRenderTarget_;
|
||||
std::vector<Ptr<RenderTarget>> renderTargets_;
|
||||
|
|
@ -326,8 +326,7 @@ private:
|
|||
// ============================================================================
|
||||
// 便捷宏
|
||||
// ============================================================================
|
||||
#define E2D_RENDER_TARGET_STACK() ::extra2d::RenderTargetStack::getInstance()
|
||||
#define E2D_RENDER_TARGET_MANAGER() \
|
||||
::extra2d::RenderTargetManager::getInstance()
|
||||
#define E2D_RENDER_TARGET_STACK() ::extra2d::RenderTargetStack::get()
|
||||
#define E2D_RENDER_TARGET_MGR() ::extra2d::RenderTargetMgr::get()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public:
|
|||
// ------------------------------------------------------------------------
|
||||
// 单例访问
|
||||
// ------------------------------------------------------------------------
|
||||
static ShaderSystem &getInstance();
|
||||
static ShaderSystem &get();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 初始化和关闭
|
||||
|
|
@ -174,6 +174,6 @@ private:
|
|||
// ============================================================================
|
||||
// 便捷宏
|
||||
// ============================================================================
|
||||
#define E2D_SHADER_SYSTEM() ::extra2d::ShaderSystem::getInstance()
|
||||
#define E2D_SHADER_SYSTEM() ::extra2d::ShaderSystem::get()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -146,9 +146,9 @@ private:
|
|||
/**
|
||||
* @brief 全局图集管理器(单例)
|
||||
*/
|
||||
class TextureAtlasManager {
|
||||
class TextureAtlasMgr {
|
||||
public:
|
||||
static TextureAtlasManager& getInstance();
|
||||
static TextureAtlasMgr& get();
|
||||
|
||||
// 获取主图集
|
||||
TextureAtlas& getAtlas() { return atlas_; }
|
||||
|
|
@ -172,11 +172,11 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
TextureAtlasManager() = default;
|
||||
~TextureAtlasManager() = default;
|
||||
TextureAtlasMgr() = default;
|
||||
~TextureAtlasMgr() = default;
|
||||
|
||||
TextureAtlasManager(const TextureAtlasManager&) = delete;
|
||||
TextureAtlasManager& operator=(const TextureAtlasManager&) = delete;
|
||||
TextureAtlasMgr(const TextureAtlasMgr&) = delete;
|
||||
TextureAtlasMgr& operator=(const TextureAtlasMgr&) = delete;
|
||||
|
||||
TextureAtlas atlas_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -171,13 +171,13 @@ public:
|
|||
* @brief 获取视口变换矩阵
|
||||
* @return 视口变换矩阵(从逻辑坐标到屏幕坐标)
|
||||
*/
|
||||
glm::mat4 getViewportMatrix() const;
|
||||
glm::mat4 getMatrix() const;
|
||||
|
||||
/**
|
||||
* @brief 获取反向视口变换矩阵
|
||||
* @return 反向视口变换矩阵(从屏幕坐标到逻辑坐标)
|
||||
*/
|
||||
glm::mat4 getInverseViewportMatrix() const;
|
||||
glm::mat4 getInvMatrix() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 区域检测
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ namespace extra2d {
|
|||
// ============================================================================
|
||||
// VRAM 管理器 - 跟踪显存使用情况
|
||||
// ============================================================================
|
||||
class VRAMManager {
|
||||
class VRAMMgr {
|
||||
public:
|
||||
static VRAMManager& getInstance();
|
||||
static VRAMMgr& get();
|
||||
|
||||
// 纹理显存跟踪
|
||||
void allocTexture(size_t size);
|
||||
|
|
@ -39,10 +39,10 @@ public:
|
|||
void reset();
|
||||
|
||||
private:
|
||||
VRAMManager();
|
||||
~VRAMManager() = default;
|
||||
VRAMManager(const VRAMManager&) = delete;
|
||||
VRAMManager& operator=(const VRAMManager&) = delete;
|
||||
VRAMMgr();
|
||||
~VRAMMgr() = default;
|
||||
VRAMMgr(const VRAMMgr&) = delete;
|
||||
VRAMMgr& operator=(const VRAMMgr&) = delete;
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
|
|
|
|||
|
|
@ -96,13 +96,13 @@ public:
|
|||
* @brief 获取逻辑坐标下的鼠标位置
|
||||
* @return 逻辑坐标
|
||||
*/
|
||||
Vec2 getMousePositionLogic() const;
|
||||
Vec2 getMousePosLogic() const;
|
||||
|
||||
/**
|
||||
* @brief 获取逻辑坐标下的触摸位置
|
||||
* @return 逻辑坐标
|
||||
*/
|
||||
Vec2 getTouchPositionLogic() const;
|
||||
Vec2 getTouchPosLogic() const;
|
||||
|
||||
/**
|
||||
* @brief 获取逻辑坐标下的鼠标增量
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ public:
|
|||
// 窗口属性
|
||||
void setTitle(const std::string &title);
|
||||
void setSize(int width, int height);
|
||||
void setPosition(int x, int y);
|
||||
void setPos(int x, int y);
|
||||
void setFullscreen(bool fullscreen);
|
||||
void setVSync(bool enabled);
|
||||
void setResizable(bool resizable);
|
||||
|
|
|
|||
|
|
@ -37,19 +37,19 @@ public:
|
|||
|
||||
void removeChild(Ptr<Node> child);
|
||||
void removeChildByName(const std::string &name);
|
||||
void removeFromParent();
|
||||
void removeAllChildren();
|
||||
void detach();
|
||||
void clearChildren();
|
||||
|
||||
Ptr<Node> getParent() const { return parent_.lock(); }
|
||||
const std::vector<Ptr<Node>> &getChildren() const { return children_; }
|
||||
Ptr<Node> getChildByName(const std::string &name) const;
|
||||
Ptr<Node> getChildByTag(int tag) const;
|
||||
Ptr<Node> findChild(const std::string &name) const;
|
||||
Ptr<Node> findChildByTag(int tag) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 变换属性
|
||||
// ------------------------------------------------------------------------
|
||||
void setPosition(const Vec2 &pos);
|
||||
void setPosition(float x, float y);
|
||||
void setPos(const Vec2 &pos);
|
||||
void setPos(float x, float y);
|
||||
Vec2 getPosition() const { return position_; }
|
||||
|
||||
void setRotation(float degrees);
|
||||
|
|
@ -99,8 +99,8 @@ public:
|
|||
// ------------------------------------------------------------------------
|
||||
// 世界变换
|
||||
// ------------------------------------------------------------------------
|
||||
Vec2 convertToWorldSpace(const Vec2 &localPos) const;
|
||||
Vec2 convertToNodeSpace(const Vec2 &worldPos) const;
|
||||
Vec2 toWorld(const Vec2 &localPos) const;
|
||||
Vec2 toLocal(const Vec2 &worldPos) const;
|
||||
|
||||
glm::mat4 getLocalTransform() const;
|
||||
glm::mat4 getWorldTransform() const;
|
||||
|
|
@ -114,7 +114,7 @@ public:
|
|||
* @brief 批量更新变换矩阵
|
||||
* 在渲染前统一计算所有脏节点的变换矩阵,避免逐节点计算时的重复递归
|
||||
*/
|
||||
void batchUpdateTransforms();
|
||||
void batchTransforms();
|
||||
|
||||
/**
|
||||
* @brief 获取变换脏标记状态
|
||||
|
|
@ -144,7 +144,7 @@ public:
|
|||
// ------------------------------------------------------------------------
|
||||
// 边界框
|
||||
// ------------------------------------------------------------------------
|
||||
virtual Rect getBoundingBox() const;
|
||||
virtual Rect getBounds() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 事件系统
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class SceneManager {
|
|||
public:
|
||||
using TransitionCallback = std::function<void()>;
|
||||
|
||||
static SceneManager &getInstance();
|
||||
static SceneManager &get();
|
||||
|
||||
void runWithScene(Ptr<Scene> scene);
|
||||
void replaceScene(Ptr<Scene> scene);
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ public:
|
|||
void addPoint(const Vec2 &point);
|
||||
void clearPoints();
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
Rect getBounds() const override;
|
||||
|
||||
protected:
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public:
|
|||
static Ptr<Sprite> create(Ptr<Texture> texture);
|
||||
static Ptr<Sprite> create(Ptr<Texture> texture, const Rect &rect);
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
Rect getBounds() const override;
|
||||
|
||||
protected:
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace extra2d {
|
|||
class Random {
|
||||
public:
|
||||
/// 获取单例实例
|
||||
static Random &getInstance();
|
||||
static Random &get();
|
||||
|
||||
/// 设置随机种子
|
||||
void setSeed(uint32 seed);
|
||||
|
|
@ -59,27 +59,27 @@ private:
|
|||
// ============================================================================
|
||||
|
||||
/// 获取 [0, 1) 范围内的随机浮点数
|
||||
inline float randomFloat() { return Random::getInstance().getFloat(); }
|
||||
inline float randomFloat() { return Random::get().getFloat(); }
|
||||
|
||||
/// 获取 [min, max] 范围内的随机浮点数
|
||||
inline float randomFloat(float min, float max) {
|
||||
return Random::getInstance().getFloat(min, max);
|
||||
return Random::get().getFloat(min, max);
|
||||
}
|
||||
|
||||
/// 获取 [0, max] 范围内的随机整数
|
||||
inline int randomInt(int max) { return Random::getInstance().getInt(max); }
|
||||
inline int randomInt(int max) { return Random::get().getInt(max); }
|
||||
|
||||
/// 获取 [min, max] 范围内的随机整数
|
||||
inline int randomInt(int min, int max) {
|
||||
return Random::getInstance().getInt(min, max);
|
||||
return Random::get().getInt(min, max);
|
||||
}
|
||||
|
||||
/// 获取随机布尔值
|
||||
inline bool randomBool() { return Random::getInstance().getBool(); }
|
||||
inline bool randomBool() { return Random::get().getBool(); }
|
||||
|
||||
/// 获取随机布尔值(带概率)
|
||||
inline bool randomBool(float probability) {
|
||||
return Random::getInstance().getBool(probability);
|
||||
return Random::get().getBool(probability);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ namespace extra2d {
|
|||
|
||||
/**
|
||||
* @brief 获取当前时间(秒)
|
||||
* @return 从某个固定时间点开始的秒数
|
||||
*
|
||||
* 使用高精度时钟获取当前时间,在Switch平台使用clock_gettime,
|
||||
* 其他平台使用std::chrono::steady_clock
|
||||
*/
|
||||
static double getTimeSeconds() {
|
||||
#ifdef __SWITCH__
|
||||
|
|
@ -37,13 +41,27 @@ static double getTimeSeconds() {
|
|||
#endif
|
||||
}
|
||||
|
||||
Application &Application::instance() {
|
||||
/**
|
||||
* @brief 获取Application单例实例
|
||||
* @return Application单例的引用
|
||||
*/
|
||||
Application &Application::get() {
|
||||
static Application instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数,自动关闭应用程序
|
||||
*/
|
||||
Application::~Application() { shutdown(); }
|
||||
|
||||
/**
|
||||
* @brief 初始化应用程序
|
||||
* @param config 应用程序配置
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*
|
||||
* 初始化窗口、渲染器、场景管理器、定时器管理器、事件系统、相机和视口适配器等核心组件
|
||||
*/
|
||||
bool Application::init(const AppConfig &config) {
|
||||
if (initialized_) {
|
||||
E2D_LOG_WARN("Application already initialized");
|
||||
|
|
@ -156,13 +174,18 @@ bool Application::init(const AppConfig &config) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 关闭应用程序
|
||||
*
|
||||
* 释放所有资源,包括场景管理器、渲染器、窗口等,并关闭平台相关服务
|
||||
*/
|
||||
void Application::shutdown() {
|
||||
if (!initialized_)
|
||||
return;
|
||||
|
||||
E2D_LOG_INFO("Shutting down application...");
|
||||
|
||||
VRAMManager::getInstance().printStats();
|
||||
VRAMMgr::get().printStats();
|
||||
|
||||
if (sceneManager_) {
|
||||
sceneManager_->end();
|
||||
|
|
@ -207,6 +230,11 @@ void Application::shutdown() {
|
|||
E2D_LOG_INFO("Application shutdown complete");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 运行应用程序主循环
|
||||
*
|
||||
* 进入应用程序主循环,持续处理事件、更新和渲染直到应用程序退出
|
||||
*/
|
||||
void Application::run() {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("Application not initialized");
|
||||
|
|
@ -226,11 +254,21 @@ void Application::run() {
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 请求退出应用程序
|
||||
*
|
||||
* 设置退出标志,主循环将在下一次迭代时退出
|
||||
*/
|
||||
void Application::quit() {
|
||||
shouldQuit_ = true;
|
||||
running_ = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 暂停应用程序
|
||||
*
|
||||
* 暂停应用程序更新,渲染继续进行
|
||||
*/
|
||||
void Application::pause() {
|
||||
if (!paused_) {
|
||||
paused_ = true;
|
||||
|
|
@ -238,6 +276,11 @@ void Application::pause() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 恢复应用程序
|
||||
*
|
||||
* 恢复应用程序更新,重置帧时间以避免大的deltaTime跳跃
|
||||
*/
|
||||
void Application::resume() {
|
||||
if (paused_) {
|
||||
paused_ = false;
|
||||
|
|
@ -246,6 +289,11 @@ void Application::resume() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 主循环迭代
|
||||
*
|
||||
* 执行一次主循环迭代,包括计算帧时间、处理事件、更新和渲染
|
||||
*/
|
||||
void Application::mainLoop() {
|
||||
double currentTime = getTimeSeconds();
|
||||
deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
|
||||
|
|
@ -284,6 +332,11 @@ void Application::mainLoop() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新应用程序状态
|
||||
*
|
||||
* 更新定时器管理器和场景管理器
|
||||
*/
|
||||
void Application::update() {
|
||||
if (timerManager_) {
|
||||
timerManager_->update(deltaTime_);
|
||||
|
|
@ -294,6 +347,11 @@ void Application::update() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染应用程序画面
|
||||
*
|
||||
* 设置视口并渲染当前场景
|
||||
*/
|
||||
void Application::render() {
|
||||
if (!renderer_) {
|
||||
E2D_LOG_ERROR("Render failed: renderer is null");
|
||||
|
|
@ -318,20 +376,54 @@ void Application::render() {
|
|||
window_->swapBuffers();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取输入管理器
|
||||
* @return 输入管理器的引用
|
||||
*/
|
||||
Input &Application::input() { return *window_->getInput(); }
|
||||
|
||||
/**
|
||||
* @brief 获取场景管理器
|
||||
* @return 场景管理器的引用
|
||||
*/
|
||||
SceneManager &Application::scenes() { return *sceneManager_; }
|
||||
|
||||
/**
|
||||
* @brief 获取定时器管理器
|
||||
* @return 定时器管理器的引用
|
||||
*/
|
||||
TimerManager &Application::timers() { return *timerManager_; }
|
||||
|
||||
/**
|
||||
* @brief 获取事件队列
|
||||
* @return 事件队列的引用
|
||||
*/
|
||||
EventQueue &Application::eventQueue() { return *eventQueue_; }
|
||||
|
||||
/**
|
||||
* @brief 获取事件分发器
|
||||
* @return 事件分发器的引用
|
||||
*/
|
||||
EventDispatcher &Application::eventDispatcher() { return *eventDispatcher_; }
|
||||
|
||||
/**
|
||||
* @brief 获取相机
|
||||
* @return 相机的引用
|
||||
*/
|
||||
Camera &Application::camera() { return *camera_; }
|
||||
|
||||
/**
|
||||
* @brief 获取视口适配器
|
||||
* @return 视口适配器的引用
|
||||
*/
|
||||
ViewportAdapter &Application::viewportAdapter() { return *viewportAdapter_; }
|
||||
|
||||
/**
|
||||
* @brief 进入指定场景
|
||||
* @param scene 要进入的场景
|
||||
*
|
||||
* 设置场景的视口大小并将其设置为当前场景
|
||||
*/
|
||||
void Application::enterScene(Ptr<Scene> scene) {
|
||||
if (sceneManager_ && scene) {
|
||||
scene->setViewportSize(static_cast<float>(window_->getWidth()),
|
||||
|
|
|
|||
|
|
@ -2,6 +2,15 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 创建窗口大小改变事件
|
||||
*
|
||||
* 创建一个表示窗口尺寸变化的Event对象
|
||||
*
|
||||
* @param width 新的窗口宽度(像素)
|
||||
* @param height 新的窗口高度(像素)
|
||||
* @return 包含窗口大小改变信息的Event对象
|
||||
*/
|
||||
Event Event::createWindowResize(int width, int height) {
|
||||
Event event;
|
||||
event.type = EventType::WindowResize;
|
||||
|
|
@ -9,12 +18,29 @@ Event Event::createWindowResize(int width, int height) {
|
|||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建窗口关闭事件
|
||||
*
|
||||
* 创建一个表示窗口请求关闭的Event对象
|
||||
*
|
||||
* @return 表示窗口关闭请求的Event对象
|
||||
*/
|
||||
Event Event::createWindowClose() {
|
||||
Event event;
|
||||
event.type = EventType::WindowClose;
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建键盘按键按下事件
|
||||
*
|
||||
* 创建一个表示键盘按键被按下的Event对象
|
||||
*
|
||||
* @param keyCode 按键码
|
||||
* @param scancode 扫描码
|
||||
* @param mods 修饰键状态(如Shift、Ctrl等)
|
||||
* @return 包含按键按下信息的Event对象
|
||||
*/
|
||||
Event Event::createKeyPress(int keyCode, int scancode, int mods) {
|
||||
Event event;
|
||||
event.type = EventType::KeyPressed;
|
||||
|
|
@ -22,6 +48,16 @@ Event Event::createKeyPress(int keyCode, int scancode, int mods) {
|
|||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建键盘按键释放事件
|
||||
*
|
||||
* 创建一个表示键盘按键被释放的Event对象
|
||||
*
|
||||
* @param keyCode 按键码
|
||||
* @param scancode 扫描码
|
||||
* @param mods 修饰键状态(如Shift、Ctrl等)
|
||||
* @return 包含按键释放信息的Event对象
|
||||
*/
|
||||
Event Event::createKeyRelease(int keyCode, int scancode, int mods) {
|
||||
Event event;
|
||||
event.type = EventType::KeyReleased;
|
||||
|
|
@ -29,6 +65,16 @@ Event Event::createKeyRelease(int keyCode, int scancode, int mods) {
|
|||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建鼠标按钮按下事件
|
||||
*
|
||||
* 创建一个表示鼠标按钮被按下的Event对象
|
||||
*
|
||||
* @param button 鼠标按钮编号
|
||||
* @param mods 修饰键状态
|
||||
* @param pos 鼠标按下时的位置坐标
|
||||
* @return 包含鼠标按钮按下信息的Event对象
|
||||
*/
|
||||
Event Event::createMouseButtonPress(int button, int mods, const Vec2 &pos) {
|
||||
Event event;
|
||||
event.type = EventType::MouseButtonPressed;
|
||||
|
|
@ -36,6 +82,16 @@ Event Event::createMouseButtonPress(int button, int mods, const Vec2 &pos) {
|
|||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建鼠标按钮释放事件
|
||||
*
|
||||
* 创建一个表示鼠标按钮被释放的Event对象
|
||||
*
|
||||
* @param button 鼠标按钮编号
|
||||
* @param mods 修饰键状态
|
||||
* @param pos 鼠标释放时的位置坐标
|
||||
* @return 包含鼠标按钮释放信息的Event对象
|
||||
*/
|
||||
Event Event::createMouseButtonRelease(int button, int mods, const Vec2 &pos) {
|
||||
Event event;
|
||||
event.type = EventType::MouseButtonReleased;
|
||||
|
|
@ -43,6 +99,15 @@ Event Event::createMouseButtonRelease(int button, int mods, const Vec2 &pos) {
|
|||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建鼠标移动事件
|
||||
*
|
||||
* 创建一个表示鼠标移动的Event对象
|
||||
*
|
||||
* @param pos 鼠标当前位置坐标
|
||||
* @param delta 鼠标移动的位移量
|
||||
* @return 包含鼠标移动信息的Event对象
|
||||
*/
|
||||
Event Event::createMouseMove(const Vec2 &pos, const Vec2 &delta) {
|
||||
Event event;
|
||||
event.type = EventType::MouseMoved;
|
||||
|
|
@ -50,6 +115,15 @@ Event Event::createMouseMove(const Vec2 &pos, const Vec2 &delta) {
|
|||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建鼠标滚轮滚动事件
|
||||
*
|
||||
* 创建一个表示鼠标滚轮滚动的Event对象
|
||||
*
|
||||
* @param offset 滚轮滚动的偏移量
|
||||
* @param pos 滚动时鼠标的位置坐标
|
||||
* @return 包含鼠标滚轮滚动信息的Event对象
|
||||
*/
|
||||
Event Event::createMouseScroll(const Vec2 &offset, const Vec2 &pos) {
|
||||
Event event;
|
||||
event.type = EventType::MouseScrolled;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,22 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 初始化事件分发器,设置下一个监听器ID为1
|
||||
*/
|
||||
EventDispatcher::EventDispatcher() : nextId_(1) {}
|
||||
|
||||
/**
|
||||
* @brief 添加事件监听器
|
||||
*
|
||||
* 为指定的事件类型注册一个回调函数,返回监听器ID用于后续移除
|
||||
*
|
||||
* @param type 要监听的事件类型
|
||||
* @param callback 事件触发时调用的回调函数
|
||||
* @return 新注册监听器的唯一ID
|
||||
*/
|
||||
ListenerId EventDispatcher::addListener(EventType type,
|
||||
EventCallback callback) {
|
||||
ListenerId id = nextId_++;
|
||||
|
|
@ -12,6 +26,13 @@ ListenerId EventDispatcher::addListener(EventType type,
|
|||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移除指定的事件监听器
|
||||
*
|
||||
* 根据监听器ID移除对应的事件监听器
|
||||
*
|
||||
* @param id 要移除的监听器ID
|
||||
*/
|
||||
void EventDispatcher::removeListener(ListenerId id) {
|
||||
for (auto &[type, listeners] : listeners_) {
|
||||
auto it = std::remove_if(listeners.begin(), listeners.end(),
|
||||
|
|
@ -23,12 +44,31 @@ void EventDispatcher::removeListener(ListenerId id) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移除指定类型的所有监听器
|
||||
*
|
||||
* 移除某个事件类型下的所有已注册监听器
|
||||
*
|
||||
* @param type 要移除监听器的事件类型
|
||||
*/
|
||||
void EventDispatcher::removeAllListeners(EventType type) {
|
||||
listeners_.erase(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移除所有监听器
|
||||
*
|
||||
* 清除事件分发器中所有已注册的监听器
|
||||
*/
|
||||
void EventDispatcher::removeAllListeners() { listeners_.clear(); }
|
||||
|
||||
/**
|
||||
* @brief 分发事件
|
||||
*
|
||||
* 将事件分发给对应类型的所有监听器,直到事件被标记为已处理或所有监听器执行完毕
|
||||
*
|
||||
* @param event 要分发的事件对象(可修改)
|
||||
*/
|
||||
void EventDispatcher::dispatch(Event &event) {
|
||||
auto it = listeners_.find(event.type);
|
||||
if (it != listeners_.end()) {
|
||||
|
|
@ -40,11 +80,25 @@ void EventDispatcher::dispatch(Event &event) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 分发事件(常量版本)
|
||||
*
|
||||
* 创建事件的副本并分发,适用于常量事件对象
|
||||
*
|
||||
* @param event 要分发的常量事件对象
|
||||
*/
|
||||
void EventDispatcher::dispatch(const Event &event) {
|
||||
Event mutableEvent = event;
|
||||
dispatch(mutableEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理事件队列
|
||||
*
|
||||
* 从事件队列中依次取出所有事件并分发
|
||||
*
|
||||
* @param queue 要处理的事件队列
|
||||
*/
|
||||
void EventDispatcher::processQueue(EventQueue &queue) {
|
||||
Event event;
|
||||
while (queue.poll(event)) {
|
||||
|
|
@ -52,11 +106,26 @@ void EventDispatcher::processQueue(EventQueue &queue) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定类型监听器的数量
|
||||
*
|
||||
* 返回某个事件类型下已注册的监听器数量
|
||||
*
|
||||
* @param type 要查询的事件类型
|
||||
* @return 该类型下的监听器数量
|
||||
*/
|
||||
size_t EventDispatcher::getListenerCount(EventType type) const {
|
||||
auto it = listeners_.find(type);
|
||||
return (it != listeners_.end()) ? it->second.size() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取所有监听器的总数量
|
||||
*
|
||||
* 返回所有事件类型的监听器总数
|
||||
*
|
||||
* @return 所有监听器的总数量
|
||||
*/
|
||||
size_t EventDispatcher::getTotalListenerCount() const {
|
||||
size_t count = 0;
|
||||
for (const auto &[type, listeners] : listeners_) {
|
||||
|
|
|
|||
|
|
@ -2,18 +2,45 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 构造一个空的事件队列对象
|
||||
*/
|
||||
EventQueue::EventQueue() = default;
|
||||
|
||||
/**
|
||||
* @brief 将事件压入队列(左值引用版本)
|
||||
*
|
||||
* 将事件以拷贝方式添加到队列末尾,线程安全
|
||||
*
|
||||
* @param event 要添加的事件对象
|
||||
*/
|
||||
void EventQueue::push(const Event &event) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
queue_.push(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将事件压入队列(右值引用版本)
|
||||
*
|
||||
* 将事件以移动方式添加到队列末尾,线程安全
|
||||
*
|
||||
* @param event 要添加的事件对象(右值引用)
|
||||
*/
|
||||
void EventQueue::push(Event &&event) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
queue_.push(std::move(event));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从队列中取出事件
|
||||
*
|
||||
* 从队列头部取出一个事件,如果队列不为空则移除该事件,线程安全
|
||||
*
|
||||
* @param event 输出参数,用于存储取出的事件
|
||||
* @return 如果成功取出事件返回true,队列为空返回false
|
||||
*/
|
||||
bool EventQueue::poll(Event &event) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (queue_.empty()) {
|
||||
|
|
@ -24,6 +51,14 @@ bool EventQueue::poll(Event &event) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查看队列头部事件
|
||||
*
|
||||
* 获取队列头部的事件但不移除,线程安全
|
||||
*
|
||||
* @param event 输出参数,用于存储查看到的事件
|
||||
* @return 如果队列不为空返回true,队列为空返回false
|
||||
*/
|
||||
bool EventQueue::peek(Event &event) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (queue_.empty()) {
|
||||
|
|
@ -33,6 +68,11 @@ bool EventQueue::peek(Event &event) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空队列
|
||||
*
|
||||
* 移除队列中的所有事件,线程安全
|
||||
*/
|
||||
void EventQueue::clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
while (!queue_.empty()) {
|
||||
|
|
@ -40,11 +80,25 @@ void EventQueue::clear() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查队列是否为空
|
||||
*
|
||||
* 线程安全地检查队列中是否有事件
|
||||
*
|
||||
* @return 如果队列为空返回true,否则返回false
|
||||
*/
|
||||
bool EventQueue::empty() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return queue_.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取队列中的事件数量
|
||||
*
|
||||
* 线程安全地获取队列中当前存储的事件数量
|
||||
*
|
||||
* @return 队列中的事件数量
|
||||
*/
|
||||
size_t EventQueue::size() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return queue_.size();
|
||||
|
|
|
|||
|
|
@ -2,9 +2,29 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*
|
||||
* 创建指定尺寸的Alpha遮罩,初始时所有像素均为不透明(值为255)
|
||||
*
|
||||
* @param width 遮罩宽度(像素)
|
||||
* @param height 遮罩高度(像素)
|
||||
*/
|
||||
AlphaMask::AlphaMask(int width, int height)
|
||||
: width_(width), height_(height), data_(width * height, 255) {}
|
||||
|
||||
/**
|
||||
* @brief 从像素数据创建Alpha遮罩
|
||||
*
|
||||
* 根据输入的像素数据提取Alpha通道创建遮罩,
|
||||
* 支持RGBA(4通道)、RGB(3通道)和灰度(1通道)格式
|
||||
*
|
||||
* @param pixels 像素数据指针
|
||||
* @param width 图像宽度
|
||||
* @param height 图像高度
|
||||
* @param channels 通道数量(1、3或4)
|
||||
* @return 创建的Alpha遮罩对象
|
||||
*/
|
||||
AlphaMask AlphaMask::createFromPixels(const uint8_t *pixels, int width,
|
||||
int height, int channels) {
|
||||
AlphaMask mask(width, height);
|
||||
|
|
@ -37,6 +57,15 @@ AlphaMask AlphaMask::createFromPixels(const uint8_t *pixels, int width,
|
|||
return mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定位置的Alpha值
|
||||
*
|
||||
* 返回指定坐标处的Alpha值,如果坐标无效则返回0
|
||||
*
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
* @return Alpha值(0-255),坐标无效时返回0
|
||||
*/
|
||||
uint8_t AlphaMask::getAlpha(int x, int y) const {
|
||||
if (!isValid(x, y)) {
|
||||
return 0;
|
||||
|
|
@ -44,10 +73,29 @@ uint8_t AlphaMask::getAlpha(int x, int y) const {
|
|||
return data_[y * width_ + x];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查指定位置是否不透明
|
||||
*
|
||||
* 判断指定坐标处的Alpha值是否大于等于给定的阈值
|
||||
*
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
* @param threshold 不透明度阈值(默认为255,即完全不透明)
|
||||
* @return 如果Alpha值大于等于阈值返回true,否则返回false
|
||||
*/
|
||||
bool AlphaMask::isOpaque(int x, int y, uint8_t threshold) const {
|
||||
return getAlpha(x, y) >= threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查坐标是否有效
|
||||
*
|
||||
* 判断给定的坐标是否在遮罩的有效范围内
|
||||
*
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
* @return 如果坐标在有效范围内返回true,否则返回false
|
||||
*/
|
||||
bool AlphaMask::isValid(int x, int y) const {
|
||||
return x >= 0 && x < width_ && y >= 0 && y < height_;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,97 @@
|
|||
#include <algorithm>
|
||||
#include <extra2d/graphics/camera.h>
|
||||
#include <extra2d/graphics/viewport_adapter.h>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/matrix_inverse.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 创建一个默认的正交相机,视口范围为 (-1, -1) 到 (1, 1)
|
||||
*/
|
||||
Camera::Camera() : left_(-1.0f), right_(1.0f), bottom_(-1.0f), top_(1.0f) {}
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param left 视口左边界
|
||||
* @param right 视口右边界
|
||||
* @param bottom 视口底边界
|
||||
* @param top 视口顶边界
|
||||
*
|
||||
* 创建一个指定视口范围的正交相机
|
||||
*/
|
||||
Camera::Camera(float left, float right, float bottom, float top)
|
||||
: left_(left), right_(right), bottom_(bottom), top_(top) {}
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param viewport 视口尺寸
|
||||
*
|
||||
* 根据视口尺寸创建相机,视口原点在左上角
|
||||
*/
|
||||
Camera::Camera(const Size &viewport)
|
||||
: left_(0.0f), right_(viewport.width), bottom_(viewport.height),
|
||||
top_(0.0f) {}
|
||||
|
||||
void Camera::setPosition(const Vec2 &position) {
|
||||
/**
|
||||
* @brief 设置相机位置
|
||||
* @param position 新的位置坐标
|
||||
*
|
||||
* 设置相机在世界空间中的位置,会标记视图矩阵为脏
|
||||
*/
|
||||
void Camera::setPos(const Vec2 &position) {
|
||||
position_ = position;
|
||||
viewDirty_ = true;
|
||||
}
|
||||
|
||||
void Camera::setPosition(float x, float y) {
|
||||
/**
|
||||
* @brief 设置相机位置
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
*
|
||||
* 设置相机在世界空间中的位置,会标记视图矩阵为脏
|
||||
*/
|
||||
void Camera::setPos(float x, float y) {
|
||||
position_.x = x;
|
||||
position_.y = y;
|
||||
viewDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置相机旋转角度
|
||||
* @param degrees 旋转角度(度数)
|
||||
*
|
||||
* 设置相机的旋转角度,会标记视图矩阵为脏
|
||||
*/
|
||||
void Camera::setRotation(float degrees) {
|
||||
rotation_ = degrees;
|
||||
viewDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置相机缩放级别
|
||||
* @param zoom 缩放值(1.0为正常大小)
|
||||
*
|
||||
* 设置相机的缩放级别,会同时标记视图矩阵和投影矩阵为脏
|
||||
*/
|
||||
void Camera::setZoom(float zoom) {
|
||||
zoom_ = zoom;
|
||||
viewDirty_ = true;
|
||||
projDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视口范围
|
||||
* @param left 左边界
|
||||
* @param right 右边界
|
||||
* @param bottom 底边界
|
||||
* @param top 顶边界
|
||||
*
|
||||
* 设置相机的正交投影视口范围,会标记投影矩阵为脏
|
||||
*/
|
||||
void Camera::setViewport(float left, float right, float bottom, float top) {
|
||||
left_ = left;
|
||||
right_ = right;
|
||||
|
|
@ -45,6 +100,12 @@ void Camera::setViewport(float left, float right, float bottom, float top) {
|
|||
projDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视口范围
|
||||
* @param rect 视口矩形
|
||||
*
|
||||
* 使用矩形设置相机的正交投影视口范围,会标记投影矩阵为脏
|
||||
*/
|
||||
void Camera::setViewport(const Rect &rect) {
|
||||
left_ = rect.left();
|
||||
right_ = rect.right();
|
||||
|
|
@ -53,6 +114,12 @@ void Camera::setViewport(const Rect &rect) {
|
|||
projDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取视口矩形
|
||||
* @return 当前视口的矩形表示
|
||||
*
|
||||
* 返回当前相机的视口范围
|
||||
*/
|
||||
Rect Camera::getViewport() const {
|
||||
return Rect(left_, top_, right_ - left_, bottom_ - top_);
|
||||
}
|
||||
|
|
@ -74,15 +141,14 @@ glm::mat4 Camera::getViewMatrix() const {
|
|||
|
||||
// 2. 旋转(中间应用)
|
||||
if (rotation_ != 0.0f) {
|
||||
viewMatrix_ = glm::rotate(viewMatrix_,
|
||||
-rotation_ * DEG_TO_RAD,
|
||||
viewMatrix_ = glm::rotate(viewMatrix_, -rotation_ * DEG_TO_RAD,
|
||||
glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
// 3. 缩放(最先应用)
|
||||
if (zoom_ != 1.0f) {
|
||||
viewMatrix_ = glm::scale(viewMatrix_,
|
||||
glm::vec3(1.0f / zoom_, 1.0f / zoom_, 1.0f));
|
||||
viewMatrix_ =
|
||||
glm::scale(viewMatrix_, glm::vec3(1.0f / zoom_, 1.0f / zoom_, 1.0f));
|
||||
}
|
||||
|
||||
viewDirty_ = false;
|
||||
|
|
@ -90,6 +156,13 @@ glm::mat4 Camera::getViewMatrix() const {
|
|||
return viewMatrix_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取投影矩阵
|
||||
* @return 正交投影矩阵
|
||||
*
|
||||
* 对于2D游戏,Y轴向下增长(屏幕坐标系)
|
||||
* OpenGL默认Y轴向上,所以需要反转Y轴
|
||||
*/
|
||||
glm::mat4 Camera::getProjectionMatrix() const {
|
||||
if (projDirty_) {
|
||||
// 对于2D游戏,Y轴向下增长(屏幕坐标系)
|
||||
|
|
@ -150,32 +223,73 @@ Vec2 Camera::worldToScreen(const Vec2 &worldPos) const {
|
|||
return logicPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将屏幕坐标转换为世界坐标
|
||||
* @param x 屏幕X坐标
|
||||
* @param y 屏幕Y坐标
|
||||
* @return 世界坐标
|
||||
*/
|
||||
Vec2 Camera::screenToWorld(float x, float y) const {
|
||||
return screenToWorld(Vec2(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将世界坐标转换为屏幕坐标
|
||||
* @param x 世界X坐标
|
||||
* @param y 世界Y坐标
|
||||
* @return 屏幕坐标
|
||||
*/
|
||||
Vec2 Camera::worldToScreen(float x, float y) const {
|
||||
return worldToScreen(Vec2(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移动相机位置
|
||||
* @param offset 位置偏移量
|
||||
*
|
||||
* 按指定偏移量移动相机位置,会标记视图矩阵为脏
|
||||
*/
|
||||
void Camera::move(const Vec2 &offset) {
|
||||
position_ += offset;
|
||||
viewDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移动相机位置
|
||||
* @param x X方向偏移量
|
||||
* @param y Y方向偏移量
|
||||
*
|
||||
* 按指定偏移量移动相机位置,会标记视图矩阵为脏
|
||||
*/
|
||||
void Camera::move(float x, float y) {
|
||||
position_.x += x;
|
||||
position_.y += y;
|
||||
viewDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置相机边界限制
|
||||
* @param bounds 边界矩形
|
||||
*
|
||||
* 设置相机的移动边界,相机位置将被限制在此边界内
|
||||
*/
|
||||
void Camera::setBounds(const Rect &bounds) {
|
||||
bounds_ = bounds;
|
||||
hasBounds_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除相机边界限制
|
||||
*
|
||||
* 移除相机的移动边界限制
|
||||
*/
|
||||
void Camera::clearBounds() { hasBounds_ = false; }
|
||||
|
||||
/**
|
||||
* @brief 将相机位置限制在边界内
|
||||
*
|
||||
* 如果设置了边界,将相机位置限制在边界矩形内
|
||||
*/
|
||||
void Camera::clampToBounds() {
|
||||
if (!hasBounds_)
|
||||
return;
|
||||
|
|
@ -203,6 +317,12 @@ void Camera::clampToBounds() {
|
|||
viewDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将相机移动到目标位置
|
||||
* @param target 目标位置
|
||||
*
|
||||
* 设置相机位置到指定的世界坐标
|
||||
*/
|
||||
void Camera::lookAt(const Vec2 &target) {
|
||||
position_ = target;
|
||||
viewDirty_ = true;
|
||||
|
|
@ -218,6 +338,8 @@ void Camera::setViewportAdapter(ViewportAdapter* adapter) {
|
|||
|
||||
/**
|
||||
* @brief 根据视口适配器自动设置视口
|
||||
*
|
||||
* 如果设置了视口适配器,根据其配置自动设置相机的视口范围
|
||||
*/
|
||||
void Camera::applyViewportAdapter() {
|
||||
if (viewportAdapter_) {
|
||||
|
|
|
|||
|
|
@ -2,19 +2,41 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
GPUContext& GPUContext::getInstance() {
|
||||
/**
|
||||
* @brief 获取GPUContext单例实例
|
||||
* @return GPUContext单例的引用
|
||||
*
|
||||
* 使用静态局部变量实现线程安全的单例模式
|
||||
*/
|
||||
GPUContext& GPUContext::get() {
|
||||
static GPUContext instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 标记GPU上下文为有效状态
|
||||
*
|
||||
* 使用原子操作设置有效标志为true,使用release内存序
|
||||
*/
|
||||
void GPUContext::markValid() {
|
||||
valid_.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 标记GPU上下文为无效状态
|
||||
*
|
||||
* 使用原子操作设置有效标志为false,使用release内存序
|
||||
*/
|
||||
void GPUContext::markInvalid() {
|
||||
valid_.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查GPU上下文是否有效
|
||||
* @return 如果GPU上下文有效返回true,否则返回false
|
||||
*
|
||||
* 使用原子操作读取有效标志,使用acquire内存序
|
||||
*/
|
||||
bool GPUContext::isValid() const {
|
||||
return valid_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@ namespace extra2d {
|
|||
// ============================================================================
|
||||
// 构造函数 - 初始化字体图集
|
||||
// ============================================================================
|
||||
/**
|
||||
* @brief 构造函数,从字体文件初始化字体图集
|
||||
* @param filepath 字体文件路径
|
||||
* @param fontSize 字体大小(像素)
|
||||
* @param useSDF 是否使用有符号距离场渲染
|
||||
*/
|
||||
GLFontAtlas::GLFontAtlas(const std::string &filepath, int fontSize, bool useSDF)
|
||||
: fontSize_(fontSize), useSDF_(useSDF), currentY_(0), scale_(0.0f),
|
||||
ascent_(0.0f), descent_(0.0f), lineGap_(0.0f) {
|
||||
|
|
@ -53,11 +59,19 @@ GLFontAtlas::GLFontAtlas(const std::string &filepath, int fontSize, bool useSDF)
|
|||
// ============================================================================
|
||||
// 析构函数
|
||||
// ============================================================================
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
GLFontAtlas::~GLFontAtlas() = default;
|
||||
|
||||
// ============================================================================
|
||||
// 获取字形 - 如果字形不存在则缓存它
|
||||
// ============================================================================
|
||||
/**
|
||||
* @brief 获取字形信息,如果字形不存在则动态缓存
|
||||
* @param codepoint Unicode码点
|
||||
* @return 字形信息指针,如果获取失败返回nullptr
|
||||
*/
|
||||
const Glyph *GLFontAtlas::getGlyph(char32_t codepoint) const {
|
||||
auto it = glyphs_.find(codepoint);
|
||||
if (it == glyphs_.end()) {
|
||||
|
|
@ -70,6 +84,11 @@ const Glyph *GLFontAtlas::getGlyph(char32_t codepoint) const {
|
|||
// ============================================================================
|
||||
// 测量文本尺寸
|
||||
// ============================================================================
|
||||
/**
|
||||
* @brief 测量文本渲染后的尺寸
|
||||
* @param text 要测量的文本
|
||||
* @return 文本的宽度和高度
|
||||
*/
|
||||
Vec2 GLFontAtlas::measureText(const std::string &text) {
|
||||
float width = 0.0f;
|
||||
float height = getAscent() - getDescent();
|
||||
|
|
@ -97,6 +116,9 @@ Vec2 GLFontAtlas::measureText(const std::string &text) {
|
|||
// ============================================================================
|
||||
// 创建图集纹理 - 初始化空白纹理和矩形打包上下文
|
||||
// ============================================================================
|
||||
/**
|
||||
* @brief 创建字体图集纹理,初始化空白纹理和矩形打包上下文
|
||||
*/
|
||||
void GLFontAtlas::createAtlas() {
|
||||
// 统一使用 4 通道格式
|
||||
int channels = 4;
|
||||
|
|
@ -121,6 +143,10 @@ void GLFontAtlas::createAtlas() {
|
|||
// 缓存字形 - 渲染字形到图集并存储信息
|
||||
// 使用 stb_rect_pack 进行矩形打包
|
||||
// ============================================================================
|
||||
/**
|
||||
* @brief 缓存字形到图集,渲染字形位图并存储字形信息
|
||||
* @param codepoint Unicode码点
|
||||
*/
|
||||
void GLFontAtlas::cacheGlyph(char32_t codepoint) const {
|
||||
int advance = 0;
|
||||
stbtt_GetCodepointHMetrics(&fontInfo_, static_cast<int>(codepoint), &advance,
|
||||
|
|
|
|||
|
|
@ -59,6 +59,9 @@ static constexpr BlendState BLEND_STATES[] = {
|
|||
static constexpr size_t BLEND_STATE_COUNT =
|
||||
sizeof(BLEND_STATES) / sizeof(BLEND_STATES[0]);
|
||||
|
||||
/**
|
||||
* @brief 构造函数,初始化OpenGL渲染器成员变量
|
||||
*/
|
||||
GLRenderer::GLRenderer()
|
||||
: window_(nullptr), shapeVao_(0), shapeVbo_(0), lineVao_(0), lineVbo_(0),
|
||||
vsync_(true), shapeVertexCount_(0), currentShapeMode_(GL_TRIANGLES),
|
||||
|
|
@ -72,8 +75,16 @@ GLRenderer::GLRenderer()
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数,调用shutdown释放资源
|
||||
*/
|
||||
GLRenderer::~GLRenderer() { shutdown(); }
|
||||
|
||||
/**
|
||||
* @brief 初始化OpenGL渲染器
|
||||
* @param window 窗口指针
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool GLRenderer::init(Window *window) {
|
||||
window_ = window;
|
||||
|
||||
|
|
@ -93,7 +104,7 @@ bool GLRenderer::init(Window *window) {
|
|||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// 标记 GPU 上下文为有效
|
||||
GPUContext::getInstance().markValid();
|
||||
GPUContext::get().markValid();
|
||||
|
||||
E2D_LOG_INFO("OpenGL Renderer initialized");
|
||||
E2D_LOG_INFO("OpenGL Version: {}",
|
||||
|
|
@ -102,16 +113,19 @@ bool GLRenderer::init(Window *window) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 关闭渲染器,释放所有GPU资源
|
||||
*/
|
||||
void GLRenderer::shutdown() {
|
||||
// 标记 GPU 上下文为无效
|
||||
// 这会在销毁 OpenGL 上下文之前通知所有 GPU 资源
|
||||
GPUContext::getInstance().markInvalid();
|
||||
GPUContext::get().markInvalid();
|
||||
|
||||
spriteBatch_.shutdown();
|
||||
|
||||
if (lineVbo_ != 0) {
|
||||
glDeleteBuffers(1, &lineVbo_);
|
||||
VRAMManager::getInstance().freeBuffer(MAX_LINE_VERTICES *
|
||||
VRAMMgr::get().freeBuffer(MAX_LINE_VERTICES *
|
||||
sizeof(ShapeVertex));
|
||||
lineVbo_ = 0;
|
||||
}
|
||||
|
|
@ -121,7 +135,7 @@ void GLRenderer::shutdown() {
|
|||
}
|
||||
if (shapeVbo_ != 0) {
|
||||
glDeleteBuffers(1, &shapeVbo_);
|
||||
VRAMManager::getInstance().freeBuffer(MAX_SHAPE_VERTICES *
|
||||
VRAMMgr::get().freeBuffer(MAX_SHAPE_VERTICES *
|
||||
sizeof(ShapeVertex));
|
||||
shapeVbo_ = 0;
|
||||
}
|
||||
|
|
@ -131,12 +145,19 @@ void GLRenderer::shutdown() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 开始新帧,清除颜色缓冲区并重置统计信息
|
||||
* @param clearColor 清屏颜色
|
||||
*/
|
||||
void GLRenderer::beginFrame(const Color &clearColor) {
|
||||
glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
resetStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 结束当前帧,刷新所有待处理的渲染批次
|
||||
*/
|
||||
void GLRenderer::endFrame() {
|
||||
// 刷新所有待处理的形状批次
|
||||
flushShapeBatch();
|
||||
|
|
@ -144,16 +165,31 @@ void GLRenderer::endFrame() {
|
|||
flushLineBatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视口区域
|
||||
* @param x 视口左下角X坐标
|
||||
* @param y 视口左下角Y坐标
|
||||
* @param width 视口宽度
|
||||
* @param height 视口高度
|
||||
*/
|
||||
void GLRenderer::setViewport(int x, int y, int width, int height) {
|
||||
glViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置垂直同步
|
||||
* @param enabled true启用垂直同步,false禁用
|
||||
*/
|
||||
void GLRenderer::setVSync(bool enabled) {
|
||||
vsync_ = enabled;
|
||||
// 使用 SDL2 设置交换间隔
|
||||
SDL_GL_SetSwapInterval(enabled ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置混合模式
|
||||
* @param mode 混合模式枚举值
|
||||
*/
|
||||
void GLRenderer::setBlendMode(BlendMode mode) {
|
||||
// 状态缓存检查,避免冗余 GL 调用
|
||||
if (cachedBlendMode_ == mode) {
|
||||
|
|
@ -182,10 +218,18 @@ void GLRenderer::setBlendMode(BlendMode mode) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视图投影矩阵
|
||||
* @param matrix 4x4视图投影矩阵
|
||||
*/
|
||||
void GLRenderer::setViewProjection(const glm::mat4 &matrix) {
|
||||
viewProjection_ = matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 压入变换矩阵到变换栈
|
||||
* @param transform 变换矩阵
|
||||
*/
|
||||
void GLRenderer::pushTransform(const glm::mat4 &transform) {
|
||||
if (transformStack_.empty()) {
|
||||
transformStack_.push_back(transform);
|
||||
|
|
@ -194,12 +238,19 @@ void GLRenderer::pushTransform(const glm::mat4 &transform) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从变换栈弹出顶部变换矩阵
|
||||
*/
|
||||
void GLRenderer::popTransform() {
|
||||
if (!transformStack_.empty()) {
|
||||
transformStack_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前累积的变换矩阵
|
||||
* @return 当前变换矩阵,如果栈为空则返回单位矩阵
|
||||
*/
|
||||
glm::mat4 GLRenderer::getCurrentTransform() const {
|
||||
if (transformStack_.empty()) {
|
||||
return glm::mat4(1.0f);
|
||||
|
|
@ -207,17 +258,42 @@ glm::mat4 GLRenderer::getCurrentTransform() const {
|
|||
return transformStack_.back();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建纹理对象
|
||||
* @param width 纹理宽度
|
||||
* @param height 纹理高度
|
||||
* @param pixels 像素数据指针
|
||||
* @param channels 颜色通道数
|
||||
* @return 创建的纹理智能指针
|
||||
*/
|
||||
Ptr<Texture> GLRenderer::createTexture(int width, int height,
|
||||
const uint8_t *pixels, int channels) {
|
||||
return makePtr<GLTexture>(width, height, pixels, channels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从文件加载纹理
|
||||
* @param filepath 纹理文件路径
|
||||
* @return 加载的纹理智能指针
|
||||
*/
|
||||
Ptr<Texture> GLRenderer::loadTexture(const std::string &filepath) {
|
||||
return makePtr<GLTexture>(filepath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 开始精灵批处理
|
||||
*/
|
||||
void GLRenderer::beginSpriteBatch() { spriteBatch_.begin(viewProjection_); }
|
||||
|
||||
/**
|
||||
* @brief 绘制精灵(带完整参数)
|
||||
* @param texture 纹理引用
|
||||
* @param destRect 目标矩形(屏幕坐标)
|
||||
* @param srcRect 源矩形(纹理坐标)
|
||||
* @param tint 着色颜色
|
||||
* @param rotation 旋转角度(度)
|
||||
* @param anchor 锚点位置(0-1范围)
|
||||
*/
|
||||
void GLRenderer::drawSprite(const Texture &texture, const Rect &destRect,
|
||||
const Rect &srcRect, const Color &tint,
|
||||
float rotation, const Vec2 &anchor) {
|
||||
|
|
@ -246,6 +322,12 @@ void GLRenderer::drawSprite(const Texture &texture, const Rect &destRect,
|
|||
spriteBatch_.draw(texture, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制精灵(简化版本)
|
||||
* @param texture 纹理引用
|
||||
* @param position 绘制位置
|
||||
* @param tint 着色颜色
|
||||
*/
|
||||
void GLRenderer::drawSprite(const Texture &texture, const Vec2 &position,
|
||||
const Color &tint) {
|
||||
Rect destRect(position.x, position.y, static_cast<float>(texture.getWidth()),
|
||||
|
|
@ -255,11 +337,21 @@ void GLRenderer::drawSprite(const Texture &texture, const Vec2 &position,
|
|||
drawSprite(texture, destRect, srcRect, tint, 0.0f, Vec2(0, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 结束精灵批处理并提交绘制
|
||||
*/
|
||||
void GLRenderer::endSpriteBatch() {
|
||||
spriteBatch_.end();
|
||||
stats_.drawCalls += spriteBatch_.getDrawCallCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制线段
|
||||
* @param start 起点坐标
|
||||
* @param end 终点坐标
|
||||
* @param color 线条颜色
|
||||
* @param width 线条宽度
|
||||
*/
|
||||
void GLRenderer::drawLine(const Vec2 &start, const Vec2 &end,
|
||||
const Color &color, float width) {
|
||||
// 如果线宽改变,需要先刷新线条批次
|
||||
|
|
@ -300,6 +392,11 @@ void GLRenderer::drawRect(const Rect &rect, const Color &color, float width) {
|
|||
addLineVertex(x1, y1, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 填充矩形
|
||||
* @param rect 矩形区域
|
||||
* @param color 填充颜色
|
||||
*/
|
||||
void GLRenderer::fillRect(const Rect &rect, const Color &color) {
|
||||
// 提交当前批次(如果模式不同)
|
||||
submitShapeBatch(GL_TRIANGLES);
|
||||
|
|
@ -321,6 +418,14 @@ void GLRenderer::fillRect(const Rect &rect, const Color &color) {
|
|||
addShapeVertex(x1, y2, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制圆形边框
|
||||
* @param center 圆心坐标
|
||||
* @param radius 半径
|
||||
* @param color 边框颜色
|
||||
* @param segments 分段数
|
||||
* @param width 线条宽度
|
||||
*/
|
||||
void GLRenderer::drawCircle(const Vec2 ¢er, float radius,
|
||||
const Color &color, int segments, float width) {
|
||||
// 限制段数不超过缓存大小
|
||||
|
|
@ -348,6 +453,13 @@ void GLRenderer::drawCircle(const Vec2 ¢er, float radius,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 填充圆形
|
||||
* @param center 圆心坐标
|
||||
* @param radius 半径
|
||||
* @param color 填充颜色
|
||||
* @param segments 分段数
|
||||
*/
|
||||
void GLRenderer::fillCircle(const Vec2 ¢er, float radius,
|
||||
const Color &color, int segments) {
|
||||
// 限制段数不超过缓存大小
|
||||
|
|
@ -375,6 +487,14 @@ void GLRenderer::fillCircle(const Vec2 ¢er, float radius,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制三角形边框
|
||||
* @param p1 第一个顶点
|
||||
* @param p2 第二个顶点
|
||||
* @param p3 第三个顶点
|
||||
* @param color 边框颜色
|
||||
* @param width 线条宽度
|
||||
*/
|
||||
void GLRenderer::drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color, float width) {
|
||||
drawLine(p1, p2, color, width);
|
||||
|
|
@ -382,6 +502,13 @@ void GLRenderer::drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
|||
drawLine(p3, p1, color, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 填充三角形
|
||||
* @param p1 第一个顶点
|
||||
* @param p2 第二个顶点
|
||||
* @param p3 第三个顶点
|
||||
* @param color 填充颜色
|
||||
*/
|
||||
void GLRenderer::fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color) {
|
||||
submitShapeBatch(GL_TRIANGLES);
|
||||
|
|
@ -391,6 +518,12 @@ void GLRenderer::fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
|||
addShapeVertex(p3.x, p3.y, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制多边形边框
|
||||
* @param points 顶点数组
|
||||
* @param color 边框颜色
|
||||
* @param width 线条宽度
|
||||
*/
|
||||
void GLRenderer::drawPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color, float width) {
|
||||
if (points.size() < 2)
|
||||
|
|
@ -411,6 +544,11 @@ void GLRenderer::drawPolygon(const std::vector<Vec2> &points,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 填充多边形
|
||||
* @param points 顶点数组
|
||||
* @param color 填充颜色
|
||||
*/
|
||||
void GLRenderer::fillPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color) {
|
||||
if (points.size() < 3)
|
||||
|
|
@ -427,16 +565,38 @@ void GLRenderer::fillPolygon(const std::vector<Vec2> &points,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建字体图集
|
||||
* @param filepath 字体文件路径
|
||||
* @param fontSize 字体大小
|
||||
* @param useSDF 是否使用SDF渲染
|
||||
* @return 创建的字体图集智能指针
|
||||
*/
|
||||
Ptr<FontAtlas> GLRenderer::createFontAtlas(const std::string &filepath,
|
||||
int fontSize, bool useSDF) {
|
||||
return makePtr<GLFontAtlas>(filepath, fontSize, useSDF);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制文本(使用Vec2位置)
|
||||
* @param font 字体图集引用
|
||||
* @param text 文本内容
|
||||
* @param position 绘制位置
|
||||
* @param color 文本颜色
|
||||
*/
|
||||
void GLRenderer::drawText(const FontAtlas &font, const std::string &text,
|
||||
const Vec2 &position, const Color &color) {
|
||||
drawText(font, text, position.x, position.y, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制文本(使用浮点坐标)
|
||||
* @param font 字体图集引用
|
||||
* @param text 文本内容
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
* @param color 文本颜色
|
||||
*/
|
||||
void GLRenderer::drawText(const FontAtlas &font, const std::string &text,
|
||||
float x, float y, const Color &color) {
|
||||
float cursorX = x;
|
||||
|
|
@ -488,8 +648,14 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 重置渲染统计信息
|
||||
*/
|
||||
void GLRenderer::resetStats() { stats_ = Stats{}; }
|
||||
|
||||
/**
|
||||
* @brief 初始化形状渲染所需的OpenGL资源(VAO、VBO、着色器)
|
||||
*/
|
||||
void GLRenderer::initShapeRendering() {
|
||||
// 编译形状着色器
|
||||
shapeShader_.compileFromSource(SHAPE_VERTEX_SHADER, SHAPE_FRAGMENT_SHADER);
|
||||
|
|
@ -537,12 +703,18 @@ void GLRenderer::initShapeRendering() {
|
|||
glBindVertexArray(0);
|
||||
|
||||
// VRAM 跟踪
|
||||
VRAMManager::getInstance().allocBuffer(MAX_SHAPE_VERTICES *
|
||||
VRAMMgr::get().allocBuffer(MAX_SHAPE_VERTICES *
|
||||
sizeof(ShapeVertex));
|
||||
VRAMManager::getInstance().allocBuffer(MAX_LINE_VERTICES *
|
||||
VRAMMgr::get().allocBuffer(MAX_LINE_VERTICES *
|
||||
sizeof(ShapeVertex));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加形状顶点到缓存
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
* @param color 顶点颜色
|
||||
*/
|
||||
void GLRenderer::addShapeVertex(float x, float y, const Color &color) {
|
||||
if (shapeVertexCount_ >= MAX_SHAPE_VERTICES) {
|
||||
flushShapeBatch();
|
||||
|
|
@ -556,6 +728,12 @@ void GLRenderer::addShapeVertex(float x, float y, const Color &color) {
|
|||
v.a = color.a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加线条顶点到缓存
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
* @param color 顶点颜色
|
||||
*/
|
||||
void GLRenderer::addLineVertex(float x, float y, const Color &color) {
|
||||
if (lineVertexCount_ >= MAX_LINE_VERTICES) {
|
||||
flushLineBatch();
|
||||
|
|
@ -569,6 +747,10 @@ void GLRenderer::addLineVertex(float x, float y, const Color &color) {
|
|||
v.a = color.a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 提交形状批次(如果需要切换绘制模式)
|
||||
* @param mode OpenGL绘制模式
|
||||
*/
|
||||
void GLRenderer::submitShapeBatch(GLenum mode) {
|
||||
if (shapeVertexCount_ == 0)
|
||||
return;
|
||||
|
|
@ -580,6 +762,9 @@ void GLRenderer::submitShapeBatch(GLenum mode) {
|
|||
currentShapeMode_ = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 刷新形状批次,执行实际的OpenGL绘制调用
|
||||
*/
|
||||
void GLRenderer::flushShapeBatch() {
|
||||
if (shapeVertexCount_ == 0)
|
||||
return;
|
||||
|
|
@ -600,6 +785,9 @@ void GLRenderer::flushShapeBatch() {
|
|||
shapeVertexCount_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 刷新线条批次,执行实际的OpenGL绘制调用
|
||||
*/
|
||||
void GLRenderer::flushLineBatch() {
|
||||
if (lineVertexCount_ == 0)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -5,14 +5,26 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 构造函数,初始化着色器程序ID为0
|
||||
*/
|
||||
GLShader::GLShader() : programID_(0) {}
|
||||
|
||||
/**
|
||||
* @brief 析构函数,删除OpenGL着色器程序
|
||||
*/
|
||||
GLShader::~GLShader() {
|
||||
if (programID_ != 0) {
|
||||
glDeleteProgram(programID_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从源代码编译着色器程序
|
||||
* @param vertexSource 顶点着色器源代码
|
||||
* @param fragmentSource 片段着色器源代码
|
||||
* @return 编译成功返回true,失败返回false
|
||||
*/
|
||||
bool GLShader::compileFromSource(const char *vertexSource,
|
||||
const char *fragmentSource) {
|
||||
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource);
|
||||
|
|
@ -46,6 +58,12 @@ bool GLShader::compileFromSource(const char *vertexSource,
|
|||
return success == GL_TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从文件编译着色器程序
|
||||
* @param vertexPath 顶点着色器文件路径
|
||||
* @param fragmentPath 片段着色器文件路径
|
||||
* @return 编译成功返回true,失败返回false
|
||||
*/
|
||||
bool GLShader::compileFromFile(const std::string &vertexPath,
|
||||
const std::string &fragmentPath) {
|
||||
std::ifstream vShaderFile(vertexPath);
|
||||
|
|
@ -65,38 +83,85 @@ bool GLShader::compileFromFile(const std::string &vertexPath,
|
|||
fShaderStream.str().c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绑定(使用)当前着色器程序
|
||||
*/
|
||||
void GLShader::bind() const { glUseProgram(programID_); }
|
||||
|
||||
/**
|
||||
* @brief 解绑当前着色器程序
|
||||
*/
|
||||
void GLShader::unbind() const { glUseProgram(0); }
|
||||
|
||||
/**
|
||||
* @brief 设置布尔类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 布尔值
|
||||
*/
|
||||
void GLShader::setBool(const std::string &name, bool value) {
|
||||
glUniform1i(getUniformLocation(name), value ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置整数类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 整数值
|
||||
*/
|
||||
void GLShader::setInt(const std::string &name, int value) {
|
||||
glUniform1i(getUniformLocation(name), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置浮点类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 浮点值
|
||||
*/
|
||||
void GLShader::setFloat(const std::string &name, float value) {
|
||||
glUniform1f(getUniformLocation(name), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置二维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 二维向量值
|
||||
*/
|
||||
void GLShader::setVec2(const std::string &name, const glm::vec2 &value) {
|
||||
glUniform2fv(getUniformLocation(name), 1, &value[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置三维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 三维向量值
|
||||
*/
|
||||
void GLShader::setVec3(const std::string &name, const glm::vec3 &value) {
|
||||
glUniform3fv(getUniformLocation(name), 1, &value[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置四维向量类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 四维向量值
|
||||
*/
|
||||
void GLShader::setVec4(const std::string &name, const glm::vec4 &value) {
|
||||
glUniform4fv(getUniformLocation(name), 1, &value[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置4x4矩阵类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 4x4矩阵值
|
||||
*/
|
||||
void GLShader::setMat4(const std::string &name, const glm::mat4 &value) {
|
||||
glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, &value[0][0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 编译单个着色器
|
||||
* @param type 着色器类型(GL_VERTEX_SHADER或GL_FRAGMENT_SHADER)
|
||||
* @param source 着色器源代码
|
||||
* @return 编译成功返回着色器ID,失败返回0
|
||||
*/
|
||||
GLuint GLShader::compileShader(GLenum type, const char *source) {
|
||||
GLuint shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &source, nullptr);
|
||||
|
|
@ -115,6 +180,11 @@ GLuint GLShader::compileShader(GLenum type, const char *source) {
|
|||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取uniform变量位置,使用缓存避免重复查询
|
||||
* @param name uniform变量名
|
||||
* @return uniform位置
|
||||
*/
|
||||
GLint GLShader::getUniformLocation(const std::string &name) {
|
||||
auto it = uniformCache_.find(name);
|
||||
if (it != uniformCache_.end()) {
|
||||
|
|
|
|||
|
|
@ -8,16 +8,29 @@ namespace extra2d {
|
|||
// ============================================================================
|
||||
// 三角函数查表 - 避免每帧重复计算 sin/cos
|
||||
// ============================================================================
|
||||
/**
|
||||
* @brief 三角函数查表类,避免每帧重复计算sin/cos
|
||||
*/
|
||||
class TrigLookup {
|
||||
public:
|
||||
static constexpr size_t TABLE_SIZE = 360 * 4; // 0.25度精度
|
||||
static constexpr float INDEX_SCALE = 4.0f; // 每度4个采样点
|
||||
static constexpr float RAD_TO_INDEX = INDEX_SCALE * 180.0f / 3.14159265359f;
|
||||
|
||||
/**
|
||||
* @brief 查表获取sin值
|
||||
* @param radians 弧度值
|
||||
* @return sin值
|
||||
*/
|
||||
static float sinRad(float radians) {
|
||||
return table_.sinTable[normalizeIndexRad(radians)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查表获取cos值
|
||||
* @param radians 弧度值
|
||||
* @return cos值
|
||||
*/
|
||||
static float cosRad(float radians) {
|
||||
return table_.cosTable[normalizeIndexRad(radians)];
|
||||
}
|
||||
|
|
@ -50,6 +63,10 @@ private:
|
|||
const TrigLookup::Tables TrigLookup::table_;
|
||||
|
||||
// 静态索引生成函数
|
||||
/**
|
||||
* @brief 获取静态索引数组,用于精灵绘制
|
||||
* @return 索引数组的常量引用
|
||||
*/
|
||||
static const std::array<GLuint, GLSpriteBatch::MAX_INDICES>& getIndices() {
|
||||
static std::array<GLuint, GLSpriteBatch::MAX_INDICES> indices = []() {
|
||||
std::array<GLuint, GLSpriteBatch::MAX_INDICES> arr{};
|
||||
|
|
@ -114,13 +131,23 @@ void main() {
|
|||
}
|
||||
)";
|
||||
|
||||
/**
|
||||
* @brief 构造函数,初始化精灵批处理器成员变量
|
||||
*/
|
||||
GLSpriteBatch::GLSpriteBatch()
|
||||
: vao_(0), vbo_(0), ibo_(0), vertexCount_(0), currentTexture_(nullptr),
|
||||
currentIsSDF_(false), drawCallCount_(0), spriteCount_(0), batchCount_(0) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数,调用shutdown释放资源
|
||||
*/
|
||||
GLSpriteBatch::~GLSpriteBatch() { shutdown(); }
|
||||
|
||||
/**
|
||||
* @brief 初始化精灵批处理器,创建VAO、VBO、IBO和编译着色器
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool GLSpriteBatch::init() {
|
||||
// 创建并编译着色器
|
||||
if (!shader_.compileFromSource(SPRITE_VERTEX_SHADER,
|
||||
|
|
@ -167,6 +194,9 @@ bool GLSpriteBatch::init() {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 关闭精灵批处理器,释放OpenGL资源
|
||||
*/
|
||||
void GLSpriteBatch::shutdown() {
|
||||
if (vao_ != 0) {
|
||||
glDeleteVertexArrays(1, &vao_);
|
||||
|
|
@ -182,6 +212,10 @@ void GLSpriteBatch::shutdown() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 开始批处理,重置状态并设置视图投影矩阵
|
||||
* @param viewProjection 视图投影矩阵
|
||||
*/
|
||||
void GLSpriteBatch::begin(const glm::mat4 &viewProjection) {
|
||||
viewProjection_ = viewProjection;
|
||||
vertexCount_ = 0;
|
||||
|
|
@ -192,6 +226,12 @@ void GLSpriteBatch::begin(const glm::mat4 &viewProjection) {
|
|||
batchCount_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查是否需要刷新批次
|
||||
* @param texture 当前纹理
|
||||
* @param isSDF 是否为SDF渲染
|
||||
* @return 需要刷新返回true,否则返回false
|
||||
*/
|
||||
bool GLSpriteBatch::needsFlush(const Texture &texture, bool isSDF) const {
|
||||
if (currentTexture_ == nullptr) {
|
||||
return false;
|
||||
|
|
@ -202,6 +242,10 @@ bool GLSpriteBatch::needsFlush(const Texture &texture, bool isSDF) const {
|
|||
(vertexCount_ + VERTICES_PER_SPRITE > MAX_VERTICES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加精灵顶点到顶点缓冲区
|
||||
* @param data 精灵数据
|
||||
*/
|
||||
void GLSpriteBatch::addVertices(const SpriteData &data) {
|
||||
// 计算锚点偏移
|
||||
float anchorOffsetX = data.size.x * data.anchor.x;
|
||||
|
|
@ -254,6 +298,11 @@ void GLSpriteBatch::addVertices(const SpriteData &data) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制单个精灵
|
||||
* @param texture 纹理引用
|
||||
* @param data 精灵数据
|
||||
*/
|
||||
void GLSpriteBatch::draw(const Texture &texture, const SpriteData &data) {
|
||||
// 如果需要刷新,先提交当前批次
|
||||
if (needsFlush(texture, data.isSDF)) {
|
||||
|
|
@ -267,6 +316,11 @@ void GLSpriteBatch::draw(const Texture &texture, const SpriteData &data) {
|
|||
spriteCount_++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 批量绘制多个精灵
|
||||
* @param texture 纹理引用
|
||||
* @param sprites 精灵数据数组
|
||||
*/
|
||||
void GLSpriteBatch::drawBatch(const Texture &texture,
|
||||
const std::vector<SpriteData> &sprites) {
|
||||
if (sprites.empty()) {
|
||||
|
|
@ -303,6 +357,11 @@ void GLSpriteBatch::drawBatch(const Texture &texture,
|
|||
batchCount_++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 立即绘制精灵,不缓存
|
||||
* @param texture 纹理引用
|
||||
* @param data 精灵数据
|
||||
*/
|
||||
void GLSpriteBatch::drawImmediate(const Texture &texture,
|
||||
const SpriteData &data) {
|
||||
// 立即绘制,不缓存 - 用于需要立即显示的情况
|
||||
|
|
@ -316,12 +375,18 @@ void GLSpriteBatch::drawImmediate(const Texture &texture,
|
|||
flush(); // 立即提交
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 结束批处理,提交所有待绘制的精灵
|
||||
*/
|
||||
void GLSpriteBatch::end() {
|
||||
if (vertexCount_ > 0) {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 刷新批次,执行实际的OpenGL绘制调用
|
||||
*/
|
||||
void GLSpriteBatch::flush() {
|
||||
if (vertexCount_ == 0 || currentTexture_ == nullptr) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -80,6 +80,14 @@ struct DDSHeaderDXT10 {
|
|||
static constexpr uint32_t DDS_MAGIC = 0x20534444; // "DDS "
|
||||
static constexpr uint32_t DDPF_FOURCC = 0x04;
|
||||
|
||||
/**
|
||||
* @brief 生成四字符代码(FourCC)
|
||||
* @param a 第一个字符
|
||||
* @param b 第二个字符
|
||||
* @param c 第三个字符
|
||||
* @param d 第四个字符
|
||||
* @return 组合后的32位无符号整数
|
||||
*/
|
||||
static uint32_t makeFourCC(char a, char b, char c, char d) {
|
||||
return static_cast<uint32_t>(a) | (static_cast<uint32_t>(b) << 8) |
|
||||
(static_cast<uint32_t>(c) << 16) | (static_cast<uint32_t>(d) << 24);
|
||||
|
|
@ -89,6 +97,13 @@ static uint32_t makeFourCC(char a, char b, char c, char d) {
|
|||
// GLTexture 实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 从像素数据构造纹理对象
|
||||
* @param width 纹理宽度(像素)
|
||||
* @param height 纹理高度(像素)
|
||||
* @param pixels 像素数据指针,可为nullptr创建空纹理
|
||||
* @param channels 颜色通道数(1=R, 3=RGB, 4=RGBA)
|
||||
*/
|
||||
GLTexture::GLTexture(int width, int height, const uint8_t *pixels, int channels)
|
||||
: textureID_(0), width_(width), height_(height), channels_(channels),
|
||||
format_(PixelFormat::RGBA8), dataSize_(0) {
|
||||
|
|
@ -100,6 +115,10 @@ GLTexture::GLTexture(int width, int height, const uint8_t *pixels, int channels)
|
|||
createTexture(pixels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从文件路径构造纹理对象
|
||||
* @param filepath 纹理文件路径,支持普通图片格式和压缩格式(KTX/DDS)
|
||||
*/
|
||||
GLTexture::GLTexture(const std::string &filepath)
|
||||
: textureID_(0), width_(0), height_(0), channels_(0),
|
||||
format_(PixelFormat::RGBA8), dataSize_(0) {
|
||||
|
|
@ -133,12 +152,12 @@ GLTexture::~GLTexture() {
|
|||
if (textureID_ != 0) {
|
||||
// 检查 GPU 上下文是否仍然有效
|
||||
// 如果 OpenGL 上下文已销毁,则跳过 glDeleteTextures 调用
|
||||
if (GPUContext::getInstance().isValid()) {
|
||||
if (GPUContext::get().isValid()) {
|
||||
glDeleteTextures(1, &textureID_);
|
||||
}
|
||||
// VRAM 跟踪: 释放纹理显存(无论上下文是否有效都需要更新统计)
|
||||
if (dataSize_ > 0) {
|
||||
VRAMManager::getInstance().freeTexture(dataSize_);
|
||||
VRAMMgr::get().freeTexture(dataSize_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -164,8 +183,15 @@ void GLTexture::bind(unsigned int slot) const {
|
|||
glBindTexture(GL_TEXTURE_2D, textureID_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解绑当前纹理
|
||||
*/
|
||||
void GLTexture::unbind() const { glBindTexture(GL_TEXTURE_2D, 0); }
|
||||
|
||||
/**
|
||||
* @brief 创建OpenGL纹理对象并上传像素数据
|
||||
* @param pixels 像素数据指针
|
||||
*/
|
||||
void GLTexture::createTexture(const uint8_t *pixels) {
|
||||
GLenum format = GL_RGBA;
|
||||
GLenum internalFormat = GL_RGBA8;
|
||||
|
|
@ -208,7 +234,7 @@ void GLTexture::createTexture(const uint8_t *pixels) {
|
|||
|
||||
// VRAM 跟踪
|
||||
dataSize_ = static_cast<size_t>(width_ * height_ * channels_);
|
||||
VRAMManager::getInstance().allocTexture(dataSize_);
|
||||
VRAMMgr::get().allocTexture(dataSize_);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
|
@ -227,6 +253,11 @@ bool GLTexture::loadCompressed(const std::string &filepath) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 加载KTX格式压缩纹理
|
||||
* @param filepath KTX文件路径
|
||||
* @return 加载成功返回true,失败返回false
|
||||
*/
|
||||
bool GLTexture::loadKTX(const std::string &filepath) {
|
||||
std::ifstream file(filepath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
|
|
@ -316,13 +347,18 @@ bool GLTexture::loadKTX(const std::string &filepath) {
|
|||
|
||||
// VRAM 跟踪
|
||||
dataSize_ = imageSize;
|
||||
VRAMManager::getInstance().allocTexture(dataSize_);
|
||||
VRAMMgr::get().allocTexture(dataSize_);
|
||||
|
||||
E2D_LOG_INFO("Loaded compressed KTX texture: {} ({}x{}, format={:#06x})",
|
||||
filepath, width_, height_, glInternalFormat);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 加载DDS格式压缩纹理
|
||||
* @param filepath DDS文件路径
|
||||
* @return 加载成功返回true,失败返回false
|
||||
*/
|
||||
bool GLTexture::loadDDS(const std::string &filepath) {
|
||||
std::ifstream file(filepath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
|
|
@ -415,7 +451,7 @@ bool GLTexture::loadDDS(const std::string &filepath) {
|
|||
|
||||
// VRAM 跟踪
|
||||
dataSize_ = imageSize;
|
||||
VRAMManager::getInstance().allocTexture(dataSize_);
|
||||
VRAMMgr::get().allocTexture(dataSize_);
|
||||
|
||||
E2D_LOG_INFO("Loaded compressed DDS texture: {} ({}x{})", filepath, width_,
|
||||
height_);
|
||||
|
|
@ -436,6 +472,13 @@ void GLTexture::generateAlphaMask() {
|
|||
|
||||
PixelFormat GLTexture::getFormat() const { return format_; }
|
||||
|
||||
/**
|
||||
* @brief 静态工厂方法,创建指定格式的空纹理
|
||||
* @param width 纹理宽度
|
||||
* @param height 纹理高度
|
||||
* @param format 像素格式
|
||||
* @return 创建的纹理智能指针
|
||||
*/
|
||||
Ptr<Texture> GLTexture::create(int width, int height, PixelFormat format) {
|
||||
int channels = 4;
|
||||
switch (format) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,15 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 创建渲染后端实例
|
||||
*
|
||||
* 根据指定的后端类型创建对应的渲染后端实例,
|
||||
* 目前支持OpenGL后端
|
||||
*
|
||||
* @param type 渲染后端类型(如OpenGL)
|
||||
* @return 成功返回渲染后端的唯一指针,不支持的类型返回nullptr
|
||||
*/
|
||||
UniquePtr<RenderBackend> RenderBackend::create(BackendType type) {
|
||||
switch (type) {
|
||||
case BackendType::OpenGL:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,21 @@ namespace extra2d {
|
|||
// RenderCommand 便捷构造函数
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建精灵渲染命令
|
||||
*
|
||||
* 创建一个用于渲染2D精灵的RenderCommand对象,包含纹理、目标区域、源区域、
|
||||
* 着色、旋转、锚点和层级等信息
|
||||
*
|
||||
* @param tex 指向纹理对象的指针
|
||||
* @param dest 目标渲染区域
|
||||
* @param src 源纹理区域
|
||||
* @param tint 着色颜色
|
||||
* @param rot 旋转角度(弧度)
|
||||
* @param anc 锚点位置(0.0-1.0范围)
|
||||
* @param lyr 渲染层级
|
||||
* @return 配置好的RenderCommand对象
|
||||
*/
|
||||
RenderCommand RenderCommand::makeSprite(const Texture* tex, const Rect& dest,
|
||||
const Rect& src, const Color& tint,
|
||||
float rot, const Vec2& anc,
|
||||
|
|
@ -31,6 +46,18 @@ RenderCommand RenderCommand::makeSprite(const Texture* tex, const Rect& dest,
|
|||
return cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建线段渲染命令
|
||||
*
|
||||
* 创建一个用于渲染线段的RenderCommand对象
|
||||
*
|
||||
* @param s 线段起点坐标
|
||||
* @param e 线段终点坐标
|
||||
* @param c 线段颜色
|
||||
* @param w 线段宽度
|
||||
* @param lyr 渲染层级
|
||||
* @return 配置好的RenderCommand对象
|
||||
*/
|
||||
RenderCommand RenderCommand::makeLine(const Vec2& s, const Vec2& e, const Color& c,
|
||||
float w, uint32_t lyr) {
|
||||
RenderCommand cmd;
|
||||
|
|
@ -48,6 +75,18 @@ RenderCommand RenderCommand::makeLine(const Vec2& s, const Vec2& e, const Color&
|
|||
return cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建矩形渲染命令
|
||||
*
|
||||
* 创建一个用于渲染矩形的RenderCommand对象,可选择填充或描边模式
|
||||
*
|
||||
* @param r 矩形区域
|
||||
* @param c 矩形颜色
|
||||
* @param w 线条宽度(仅描边模式有效)
|
||||
* @param fill 是否填充矩形
|
||||
* @param lyr 渲染层级
|
||||
* @return 配置好的RenderCommand对象
|
||||
*/
|
||||
RenderCommand RenderCommand::makeRect(const Rect& r, const Color& c,
|
||||
float w, bool fill, uint32_t lyr) {
|
||||
RenderCommand cmd;
|
||||
|
|
@ -69,12 +108,29 @@ RenderCommand RenderCommand::makeRect(const Rect& r, const Color& c,
|
|||
// RenderCommandBuffer 实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 初始化渲染命令缓冲区,预留初始容量
|
||||
*/
|
||||
RenderCommandBuffer::RenderCommandBuffer() : nextOrder_(0) {
|
||||
commands_.reserve(INITIAL_CAPACITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 释放渲染命令缓冲区资源
|
||||
*/
|
||||
RenderCommandBuffer::~RenderCommandBuffer() = default;
|
||||
|
||||
/**
|
||||
* @brief 添加渲染命令(左值引用版本)
|
||||
*
|
||||
* 将渲染命令以拷贝方式添加到缓冲区,自动分配顺序号
|
||||
*
|
||||
* @param cmd 要添加的渲染命令
|
||||
*/
|
||||
void RenderCommandBuffer::addCommand(const RenderCommand& cmd) {
|
||||
if (commands_.size() >= MAX_CAPACITY) {
|
||||
// 缓冲区已满,可能需要立即刷新
|
||||
|
|
@ -86,6 +142,13 @@ void RenderCommandBuffer::addCommand(const RenderCommand& cmd) {
|
|||
commands_.push_back(std::move(copy));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加渲染命令(右值引用版本)
|
||||
*
|
||||
* 将渲染命令以移动方式添加到缓冲区,自动分配顺序号
|
||||
*
|
||||
* @param cmd 要添加的渲染命令(右值引用)
|
||||
*/
|
||||
void RenderCommandBuffer::addCommand(RenderCommand&& cmd) {
|
||||
if (commands_.size() >= MAX_CAPACITY) {
|
||||
return;
|
||||
|
|
@ -95,6 +158,13 @@ void RenderCommandBuffer::addCommand(RenderCommand&& cmd) {
|
|||
commands_.push_back(std::move(cmd));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 原地构造渲染命令
|
||||
*
|
||||
* 在缓冲区中直接构造一个渲染命令对象,避免额外的拷贝或移动操作
|
||||
*
|
||||
* @return 新构造的渲染命令对象的引用
|
||||
*/
|
||||
RenderCommand& RenderCommandBuffer::emplaceCommand() {
|
||||
if (commands_.size() >= MAX_CAPACITY) {
|
||||
// 如果已满,返回一个虚拟命令(不应该发生)
|
||||
|
|
@ -107,6 +177,12 @@ RenderCommand& RenderCommandBuffer::emplaceCommand() {
|
|||
return commands_.back();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 对渲染命令进行排序
|
||||
*
|
||||
* 按层级、命令类型、纹理/材质和提交顺序对命令进行排序,
|
||||
* 以优化渲染性能和批处理效率
|
||||
*/
|
||||
void RenderCommandBuffer::sortCommands() {
|
||||
// 按以下优先级排序:
|
||||
// 1. 层级 (layer) - 低层级先渲染
|
||||
|
|
@ -117,17 +193,38 @@ void RenderCommandBuffer::sortCommands() {
|
|||
std::sort(commands_.begin(), commands_.end(), compareCommands);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空缓冲区
|
||||
*
|
||||
* 移除所有渲染命令并重置顺序计数器
|
||||
*/
|
||||
void RenderCommandBuffer::clear() {
|
||||
commands_.clear();
|
||||
nextOrder_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 预留缓冲区容量
|
||||
*
|
||||
* 预先分配缓冲区内存以减少动态分配开销
|
||||
*
|
||||
* @param capacity 要预留的容量大小
|
||||
*/
|
||||
void RenderCommandBuffer::reserve(size_t capacity) {
|
||||
if (capacity <= MAX_CAPACITY) {
|
||||
commands_.reserve(capacity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染命令比较函数
|
||||
*
|
||||
* 用于排序的比较函数,按层级、类型、纹理和顺序进行比较
|
||||
*
|
||||
* @param a 第一个渲染命令
|
||||
* @param b 第二个渲染命令
|
||||
* @return 如果a应排在b前面返回true,否则返回false
|
||||
*/
|
||||
bool RenderCommandBuffer::compareCommands(const RenderCommand& a, const RenderCommand& b) {
|
||||
// 首先按层级排序
|
||||
if (a.layer != b.layer) {
|
||||
|
|
|
|||
|
|
@ -12,10 +12,26 @@ namespace extra2d {
|
|||
// RenderTarget实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 创建一个空的渲染目标对象
|
||||
*/
|
||||
RenderTarget::RenderTarget() = default;
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 销毁渲染目标并释放相关资源
|
||||
*/
|
||||
RenderTarget::~RenderTarget() { destroy(); }
|
||||
|
||||
/**
|
||||
* @brief 移动构造函数
|
||||
* @param other 源渲染目标对象
|
||||
*
|
||||
* 将其他渲染目标的资源转移到新对象
|
||||
*/
|
||||
RenderTarget::RenderTarget(RenderTarget &&other) noexcept
|
||||
: fbo_(other.fbo_), rbo_(other.rbo_),
|
||||
colorTexture_(std::move(other.colorTexture_)),
|
||||
|
|
@ -29,6 +45,13 @@ RenderTarget::RenderTarget(RenderTarget &&other) noexcept
|
|||
other.height_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移动赋值运算符
|
||||
* @param other 源渲染目标对象
|
||||
* @return 当前对象引用
|
||||
*
|
||||
* 将其他渲染目标的资源转移到当前对象
|
||||
*/
|
||||
RenderTarget &RenderTarget::operator=(RenderTarget &&other) noexcept {
|
||||
if (this != &other) {
|
||||
destroy();
|
||||
|
|
@ -52,6 +75,13 @@ RenderTarget &RenderTarget::operator=(RenderTarget &&other) noexcept {
|
|||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据配置创建渲染目标
|
||||
* @param config 渲染目标配置
|
||||
* @return 创建成功返回true,失败返回false
|
||||
*
|
||||
* 根据指定的配置参数创建帧缓冲对象
|
||||
*/
|
||||
bool RenderTarget::create(const RenderTargetConfig &config) {
|
||||
destroy();
|
||||
|
||||
|
|
@ -73,6 +103,14 @@ bool RenderTarget::create(const RenderTargetConfig &config) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从现有纹理创建渲染目标
|
||||
* @param texture 颜色纹理
|
||||
* @param hasDepth 是否创建深度缓冲
|
||||
* @return 创建成功返回true,失败返回false
|
||||
*
|
||||
* 使用现有纹理作为颜色附件创建帧缓冲对象
|
||||
*/
|
||||
bool RenderTarget::createFromTexture(Ptr<Texture> texture, bool hasDepth) {
|
||||
if (!texture || !texture->isValid()) {
|
||||
E2D_ERROR("无效的颜色纹理");
|
||||
|
|
@ -125,6 +163,11 @@ bool RenderTarget::createFromTexture(Ptr<Texture> texture, bool hasDepth) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 销毁渲染目标
|
||||
*
|
||||
* 释放帧缓冲对象和相关资源
|
||||
*/
|
||||
void RenderTarget::destroy() {
|
||||
deleteFBO();
|
||||
|
||||
|
|
@ -135,6 +178,11 @@ void RenderTarget::destroy() {
|
|||
height_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绑定渲染目标
|
||||
*
|
||||
* 将此渲染目标绑定为当前渲染目标
|
||||
*/
|
||||
void RenderTarget::bind() {
|
||||
if (!isValid()) {
|
||||
return;
|
||||
|
|
@ -144,8 +192,19 @@ void RenderTarget::bind() {
|
|||
glViewport(0, 0, width_, height_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解绑渲染目标
|
||||
*
|
||||
* 恢复默认帧缓冲
|
||||
*/
|
||||
void RenderTarget::unbind() { bindDefault(); }
|
||||
|
||||
/**
|
||||
* @brief 清除渲染目标
|
||||
* @param color 清除颜色
|
||||
*
|
||||
* 使用指定颜色清除颜色缓冲,如有深度/模板缓冲也会清除
|
||||
*/
|
||||
void RenderTarget::clear(const Color &color) {
|
||||
if (!isValid()) {
|
||||
return;
|
||||
|
|
@ -167,6 +226,15 @@ void RenderTarget::clear(const Color &color) {
|
|||
glClear(mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视口区域
|
||||
* @param x 视口左下角X坐标
|
||||
* @param y 视口左下角Y坐标
|
||||
* @param width 视口宽度
|
||||
* @param height 视口高度
|
||||
*
|
||||
* 设置渲染目标的视口区域
|
||||
*/
|
||||
void RenderTarget::setViewport(int x, int y, int width, int height) {
|
||||
if (!isValid()) {
|
||||
return;
|
||||
|
|
@ -176,6 +244,15 @@ void RenderTarget::setViewport(int x, int y, int width, int height) {
|
|||
glViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取完整视口区域
|
||||
* @param[out] x 视口左下角X坐标
|
||||
* @param[out] y 视口左下角Y坐标
|
||||
* @param[out] width 视口宽度
|
||||
* @param[out] height 视口高度
|
||||
*
|
||||
* 获取渲染目标的完整视口区域
|
||||
*/
|
||||
void RenderTarget::getFullViewport(int &x, int &y, int &width,
|
||||
int &height) const {
|
||||
x = 0;
|
||||
|
|
@ -184,6 +261,14 @@ void RenderTarget::getFullViewport(int &x, int &y, int &width,
|
|||
height = height_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 调整渲染目标大小
|
||||
* @param width 新宽度
|
||||
* @param height 新高度
|
||||
* @return 调整成功返回true,失败返回false
|
||||
*
|
||||
* 重新创建指定大小的渲染目标
|
||||
*/
|
||||
bool RenderTarget::resize(int width, int height) {
|
||||
if (!isValid()) {
|
||||
return false;
|
||||
|
|
@ -200,6 +285,12 @@ bool RenderTarget::resize(int width, int height) {
|
|||
return create(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 复制到另一个渲染目标
|
||||
* @param target 目标渲染目标
|
||||
*
|
||||
* 使用glBlitFramebuffer将内容复制到目标渲染目标
|
||||
*/
|
||||
void RenderTarget::copyTo(RenderTarget &target) {
|
||||
if (!isValid() || !target.isValid()) {
|
||||
return;
|
||||
|
|
@ -220,6 +311,14 @@ void RenderTarget::copyTo(RenderTarget &target) {
|
|||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将内容传输到另一个渲染目标
|
||||
* @param target 目标渲染目标
|
||||
* @param color 是否复制颜色缓冲
|
||||
* @param depth 是否复制深度缓冲
|
||||
*
|
||||
* 使用glBlitFramebuffer进行选择性复制
|
||||
*/
|
||||
void RenderTarget::blitTo(RenderTarget &target, bool color, bool depth) {
|
||||
if (!isValid() || !target.isValid()) {
|
||||
return;
|
||||
|
|
@ -245,6 +344,13 @@ void RenderTarget::blitTo(RenderTarget &target, bool color, bool depth) {
|
|||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 复制到屏幕
|
||||
* @param screenWidth 屏幕宽度
|
||||
* @param screenHeight 屏幕高度
|
||||
*
|
||||
* 将渲染目标内容复制到默认帧缓冲(屏幕)
|
||||
*/
|
||||
void RenderTarget::copyToScreen(int screenWidth, int screenHeight) {
|
||||
if (!isValid()) {
|
||||
return;
|
||||
|
|
@ -259,6 +365,13 @@ void RenderTarget::copyToScreen(int screenWidth, int screenHeight) {
|
|||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 保存渲染目标到文件
|
||||
* @param filepath 文件路径
|
||||
* @return 保存成功返回true,失败返回false
|
||||
*
|
||||
* 将渲染目标内容保存为PNG图片文件
|
||||
*/
|
||||
bool RenderTarget::saveToFile(const std::string &filepath) {
|
||||
if (!isValid() || !colorTexture_) {
|
||||
return false;
|
||||
|
|
@ -296,6 +409,13 @@ bool RenderTarget::saveToFile(const std::string &filepath) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据配置创建渲染目标
|
||||
* @param config 渲染目标配置
|
||||
* @return 创建成功返回渲染目标指针,失败返回nullptr
|
||||
*
|
||||
* 静态工厂方法,创建并初始化渲染目标
|
||||
*/
|
||||
Ptr<RenderTarget>
|
||||
RenderTarget::createFromConfig(const RenderTargetConfig &config) {
|
||||
auto rt = std::make_shared<RenderTarget>();
|
||||
|
|
@ -305,18 +425,35 @@ RenderTarget::createFromConfig(const RenderTargetConfig &config) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前绑定的帧缓冲对象ID
|
||||
* @return 当前绑定的FBO ID
|
||||
*
|
||||
* 查询OpenGL当前绑定的帧缓冲对象
|
||||
*/
|
||||
GLuint RenderTarget::getCurrentFBO() {
|
||||
GLint fbo = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
|
||||
return static_cast<GLuint>(fbo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绑定默认帧缓冲
|
||||
*
|
||||
* 将默认帧缓冲(屏幕)绑定为当前渲染目标
|
||||
*/
|
||||
void RenderTarget::bindDefault() { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
|
||||
|
||||
// ============================================================================
|
||||
// 内部方法
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建帧缓冲对象
|
||||
* @return 创建成功返回true,失败返回false
|
||||
*
|
||||
* 内部方法,创建FBO及相关附件
|
||||
*/
|
||||
bool RenderTarget::createFBO() {
|
||||
// 创建颜色纹理
|
||||
colorTexture_ = GLTexture::create(width_, height_, colorFormat_);
|
||||
|
|
@ -370,6 +507,11 @@ bool RenderTarget::createFBO() {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 删除帧缓冲对象
|
||||
*
|
||||
* 内部方法,删除FBO和渲染缓冲对象
|
||||
*/
|
||||
void RenderTarget::deleteFBO() {
|
||||
if (rbo_ != 0) {
|
||||
glDeleteRenderbuffers(1, &rbo_);
|
||||
|
|
@ -386,6 +528,15 @@ void RenderTarget::deleteFBO() {
|
|||
// MultisampleRenderTarget实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建多重采样渲染目标
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param samples 采样数
|
||||
* @return 创建成功返回true,失败返回false
|
||||
*
|
||||
* 创建支持多重采样抗锯齿的渲染目标
|
||||
*/
|
||||
bool MultisampleRenderTarget::create(int width, int height, int samples) {
|
||||
// 先销毁现有的
|
||||
destroy();
|
||||
|
|
@ -433,6 +584,11 @@ bool MultisampleRenderTarget::create(int width, int height, int samples) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 销毁多重采样渲染目标
|
||||
*
|
||||
* 释放多重采样渲染缓冲和帧缓冲对象
|
||||
*/
|
||||
void MultisampleRenderTarget::destroy() {
|
||||
// 删除颜色渲染缓冲
|
||||
if (colorRBO_ != 0) {
|
||||
|
|
@ -444,6 +600,12 @@ void MultisampleRenderTarget::destroy() {
|
|||
RenderTarget::destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析多重采样到目标渲染目标
|
||||
* @param target 目标渲染目标
|
||||
*
|
||||
* 将多重采样渲染目标解析到普通渲染目标
|
||||
*/
|
||||
void MultisampleRenderTarget::resolveTo(RenderTarget &target) {
|
||||
if (!isValid() || !target.isValid()) {
|
||||
return;
|
||||
|
|
@ -463,11 +625,23 @@ void MultisampleRenderTarget::resolveTo(RenderTarget &target) {
|
|||
// RenderTargetStack实现
|
||||
// ============================================================================
|
||||
|
||||
RenderTargetStack &RenderTargetStack::getInstance() {
|
||||
/**
|
||||
* @brief 获取RenderTargetStack单例实例
|
||||
* @return RenderTargetStack单例的引用
|
||||
*
|
||||
* 使用静态局部变量实现线程安全的单例模式
|
||||
*/
|
||||
RenderTargetStack &RenderTargetStack::get() {
|
||||
static RenderTargetStack instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 压入渲染目标到栈
|
||||
* @param target 渲染目标指针
|
||||
*
|
||||
* 将渲染目标压入栈并绑定为当前渲染目标
|
||||
*/
|
||||
void RenderTargetStack::push(RenderTarget *target) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
|
|
@ -477,6 +651,11 @@ void RenderTargetStack::push(RenderTarget *target) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 弹出栈顶渲染目标
|
||||
*
|
||||
* 弹出当前渲染目标并恢复前一个渲染目标
|
||||
*/
|
||||
void RenderTargetStack::pop() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
|
|
@ -492,6 +671,10 @@ void RenderTargetStack::pop() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前渲染目标
|
||||
* @return 当前渲染目标指针,栈为空返回nullptr
|
||||
*/
|
||||
RenderTarget *RenderTargetStack::getCurrent() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
|
|
@ -501,11 +684,20 @@ RenderTarget *RenderTargetStack::getCurrent() const {
|
|||
return stack_.back();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取栈大小
|
||||
* @return 栈中渲染目标的数量
|
||||
*/
|
||||
size_t RenderTargetStack::size() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return stack_.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空渲染目标栈
|
||||
*
|
||||
* 清空栈并恢复默认帧缓冲
|
||||
*/
|
||||
void RenderTargetStack::clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
stack_.clear();
|
||||
|
|
@ -513,15 +705,29 @@ void RenderTargetStack::clear() {
|
|||
}
|
||||
|
||||
// ============================================================================
|
||||
// RenderTargetManager实现
|
||||
// RenderTargetMgr实现
|
||||
// ============================================================================
|
||||
|
||||
RenderTargetManager &RenderTargetManager::getInstance() {
|
||||
static RenderTargetManager instance;
|
||||
/**
|
||||
* @brief 获取RenderTargetMgr单例实例
|
||||
* @return RenderTargetMgr单例的引用
|
||||
*
|
||||
* 使用静态局部变量实现线程安全的单例模式
|
||||
*/
|
||||
RenderTargetMgr &RenderTargetMgr::get() {
|
||||
static RenderTargetMgr instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool RenderTargetManager::init(int width, int height) {
|
||||
/**
|
||||
* @brief 初始化渲染目标管理器
|
||||
* @param width 默认渲染目标宽度
|
||||
* @param height 默认渲染目标高度
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*
|
||||
* 创建默认渲染目标
|
||||
*/
|
||||
bool RenderTargetMgr::init(int width, int height) {
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -544,7 +750,12 @@ bool RenderTargetManager::init(int width, int height) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void RenderTargetManager::shutdown() {
|
||||
/**
|
||||
* @brief 关闭渲染目标管理器
|
||||
*
|
||||
* 清理所有渲染目标资源
|
||||
*/
|
||||
void RenderTargetMgr::shutdown() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -556,8 +767,15 @@ void RenderTargetManager::shutdown() {
|
|||
E2D_INFO("渲染目标管理器已关闭");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建渲染目标
|
||||
* @param config 渲染目标配置
|
||||
* @return 创建成功返回渲染目标指针,失败返回nullptr
|
||||
*
|
||||
* 创建新的渲染目标并由管理器管理
|
||||
*/
|
||||
Ptr<RenderTarget>
|
||||
RenderTargetManager::createRenderTarget(const RenderTargetConfig &config) {
|
||||
RenderTargetMgr::createRenderTarget(const RenderTargetConfig &config) {
|
||||
if (!initialized_) {
|
||||
E2D_ERROR("渲染目标管理器未初始化");
|
||||
return nullptr;
|
||||
|
|
@ -570,7 +788,14 @@ RenderTargetManager::createRenderTarget(const RenderTargetConfig &config) {
|
|||
return rt;
|
||||
}
|
||||
|
||||
void RenderTargetManager::resize(int width, int height) {
|
||||
/**
|
||||
* @brief 调整所有渲染目标大小
|
||||
* @param width 新宽度
|
||||
* @param height 新高度
|
||||
*
|
||||
* 调整默认渲染目标的大小
|
||||
*/
|
||||
void RenderTargetMgr::resize(int width, int height) {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,14 @@ namespace extra2d {
|
|||
// ShaderPreset实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 创建水波纹效果着色器
|
||||
*
|
||||
* 创建并配置一个水波纹效果的GLShader对象,包含波动速度、振幅和频率参数
|
||||
*
|
||||
* @param params 水波纹效果参数,包含waveSpeed、waveAmplitude和waveFrequency
|
||||
* @return 成功返回配置好的着色器智能指针,失败返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> ShaderPreset::Water(const WaterParams ¶ms) {
|
||||
auto shader = std::make_shared<GLShader>();
|
||||
|
||||
|
|
@ -25,6 +33,14 @@ Ptr<GLShader> ShaderPreset::Water(const WaterParams ¶ms) {
|
|||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建描边效果着色器
|
||||
*
|
||||
* 创建并配置一个描边效果的GLShader对象,包含描边颜色和厚度参数
|
||||
*
|
||||
* @param params 描边效果参数,包含color和thickness
|
||||
* @return 成功返回配置好的着色器智能指针,失败返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> ShaderPreset::Outline(const OutlineParams ¶ms) {
|
||||
auto shader = std::make_shared<GLShader>();
|
||||
|
||||
|
|
@ -43,6 +59,14 @@ Ptr<GLShader> ShaderPreset::Outline(const OutlineParams ¶ms) {
|
|||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建扭曲效果着色器
|
||||
*
|
||||
* 创建并配置一个扭曲效果的GLShader对象,包含扭曲强度和时间缩放参数
|
||||
*
|
||||
* @param params 扭曲效果参数,包含distortionAmount和timeScale
|
||||
* @return 成功返回配置好的着色器智能指针,失败返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> ShaderPreset::Distortion(const DistortionParams ¶ms) {
|
||||
auto shader = std::make_shared<GLShader>();
|
||||
|
||||
|
|
@ -60,6 +84,14 @@ Ptr<GLShader> ShaderPreset::Distortion(const DistortionParams ¶ms) {
|
|||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建像素化效果着色器
|
||||
*
|
||||
* 创建并配置一个像素化效果的GLShader对象,包含像素大小参数
|
||||
*
|
||||
* @param params 像素化效果参数,包含pixelSize
|
||||
* @return 成功返回配置好的着色器智能指针,失败返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> ShaderPreset::Pixelate(const PixelateParams ¶ms) {
|
||||
auto shader = std::make_shared<GLShader>();
|
||||
|
||||
|
|
@ -76,6 +108,14 @@ Ptr<GLShader> ShaderPreset::Pixelate(const PixelateParams ¶ms) {
|
|||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建反相效果着色器
|
||||
*
|
||||
* 创建并配置一个颜色反相效果的GLShader对象,包含反相强度参数
|
||||
*
|
||||
* @param params 反相效果参数,包含strength
|
||||
* @return 成功返回配置好的着色器智能指针,失败返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> ShaderPreset::Invert(const InvertParams ¶ms) {
|
||||
auto shader = std::make_shared<GLShader>();
|
||||
|
||||
|
|
@ -92,6 +132,14 @@ Ptr<GLShader> ShaderPreset::Invert(const InvertParams ¶ms) {
|
|||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建灰度效果着色器
|
||||
*
|
||||
* 创建并配置一个灰度效果的GLShader对象,包含灰度强度参数
|
||||
*
|
||||
* @param params 灰度效果参数,包含intensity
|
||||
* @return 成功返回配置好的着色器智能指针,失败返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> ShaderPreset::Grayscale(const GrayscaleParams ¶ms) {
|
||||
auto shader = std::make_shared<GLShader>();
|
||||
|
||||
|
|
@ -108,6 +156,14 @@ Ptr<GLShader> ShaderPreset::Grayscale(const GrayscaleParams ¶ms) {
|
|||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建模糊效果着色器
|
||||
*
|
||||
* 创建并配置一个模糊效果的GLShader对象,包含模糊半径参数
|
||||
*
|
||||
* @param params 模糊效果参数,包含radius
|
||||
* @return 成功返回配置好的着色器智能指针,失败返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> ShaderPreset::Blur(const BlurParams ¶ms) {
|
||||
auto shader = std::make_shared<GLShader>();
|
||||
|
||||
|
|
@ -124,6 +180,15 @@ Ptr<GLShader> ShaderPreset::Blur(const BlurParams ¶ms) {
|
|||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建灰度+描边组合效果着色器
|
||||
*
|
||||
* 创建并配置一个同时应用灰度和描边效果的GLShader对象
|
||||
*
|
||||
* @param grayParams 灰度效果参数,包含intensity
|
||||
* @param outlineParams 描边效果参数,包含color和thickness
|
||||
* @return 成功返回配置好的着色器智能指针,失败返回nullptr
|
||||
*/
|
||||
Ptr<GLShader>
|
||||
ShaderPreset::GrayscaleOutline(const GrayscaleParams &grayParams,
|
||||
const OutlineParams &outlineParams) {
|
||||
|
|
@ -183,6 +248,15 @@ void main() {
|
|||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建像素化+反相组合效果着色器
|
||||
*
|
||||
* 创建并配置一个同时应用像素化和反相效果的GLShader对象
|
||||
*
|
||||
* @param pixParams 像素化效果参数,包含pixelSize
|
||||
* @param invParams 反相效果参数,包含strength
|
||||
* @return 成功返回配置好的着色器智能指针,失败返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> ShaderPreset::PixelateInvert(const PixelateParams &pixParams,
|
||||
const InvertParams &invParams) {
|
||||
// 创建组合效果的片段着色器 (GLES 3.2)
|
||||
|
|
|
|||
|
|
@ -182,11 +182,23 @@ void main() {
|
|||
// ShaderSystem实现
|
||||
// ============================================================================
|
||||
|
||||
ShaderSystem &ShaderSystem::getInstance() {
|
||||
/**
|
||||
* @brief 获取ShaderSystem单例实例
|
||||
* @return ShaderSystem单例的引用
|
||||
*
|
||||
* 使用静态局部变量实现线程安全的单例模式
|
||||
*/
|
||||
ShaderSystem &ShaderSystem::get() {
|
||||
static ShaderSystem instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化Shader系统
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*
|
||||
* 加载所有内置着色器,包括精灵、粒子、后处理和形状着色器
|
||||
*/
|
||||
bool ShaderSystem::init() {
|
||||
E2D_INFO("初始化Shader系统...");
|
||||
|
||||
|
|
@ -199,6 +211,11 @@ bool ShaderSystem::init() {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 关闭Shader系统
|
||||
*
|
||||
* 清理所有着色器资源,释放内置着色器
|
||||
*/
|
||||
void ShaderSystem::shutdown() {
|
||||
E2D_INFO("关闭Shader系统...");
|
||||
clear();
|
||||
|
|
@ -209,6 +226,12 @@ void ShaderSystem::shutdown() {
|
|||
builtinShapeShader_.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 加载内置着色器
|
||||
* @return 加载成功返回true,失败返回false
|
||||
*
|
||||
* 编译并加载所有内置着色器:精灵、粒子、后处理和形状着色器
|
||||
*/
|
||||
bool ShaderSystem::loadBuiltinShaders() {
|
||||
// 加载精灵Shader
|
||||
builtinSpriteShader_ = std::make_shared<GLShader>();
|
||||
|
|
@ -246,6 +269,15 @@ bool ShaderSystem::loadBuiltinShaders() {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从文件加载着色器
|
||||
* @param name 着色器名称
|
||||
* @param vertPath 顶点着色器文件路径
|
||||
* @param fragPath 片段着色器文件路径
|
||||
* @return 加载成功返回着色器指针,失败返回nullptr
|
||||
*
|
||||
* 从指定路径读取顶点和片段着色器源码并编译
|
||||
*/
|
||||
Ptr<GLShader> ShaderSystem::loadFromFile(const std::string &name,
|
||||
const std::string &vertPath,
|
||||
const std::string &fragPath) {
|
||||
|
|
@ -285,6 +317,15 @@ Ptr<GLShader> ShaderSystem::loadFromFile(const std::string &name,
|
|||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从源码加载着色器
|
||||
* @param name 着色器名称
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 加载成功返回着色器指针,失败返回nullptr
|
||||
*
|
||||
* 直接从源码字符串编译着色器
|
||||
*/
|
||||
Ptr<GLShader> ShaderSystem::loadFromSource(const std::string &name,
|
||||
const std::string &vertSource,
|
||||
const std::string &fragSource) {
|
||||
|
|
@ -306,6 +347,11 @@ Ptr<GLShader> ShaderSystem::loadFromSource(const std::string &name,
|
|||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定名称的着色器
|
||||
* @param name 着色器名称
|
||||
* @return 找到返回着色器指针,未找到返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> ShaderSystem::get(const std::string &name) {
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
|
|
@ -314,14 +360,36 @@ Ptr<GLShader> ShaderSystem::get(const std::string &name) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查指定名称的着色器是否存在
|
||||
* @param name 着色器名称
|
||||
* @return 存在返回true,不存在返回false
|
||||
*/
|
||||
bool ShaderSystem::has(const std::string &name) const {
|
||||
return shaders_.find(name) != shaders_.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移除指定名称的着色器
|
||||
* @param name 着色器名称
|
||||
*
|
||||
* 从着色器管理器中移除指定着色器
|
||||
*/
|
||||
void ShaderSystem::remove(const std::string &name) { shaders_.erase(name); }
|
||||
|
||||
/**
|
||||
* @brief 清空所有着色器
|
||||
*
|
||||
* 移除所有已加载的着色器
|
||||
*/
|
||||
void ShaderSystem::clear() { shaders_.clear(); }
|
||||
|
||||
/**
|
||||
* @brief 设置文件监视功能
|
||||
* @param enable 是否启用
|
||||
*
|
||||
* 启用或禁用着色器文件变化监视功能
|
||||
*/
|
||||
void ShaderSystem::setFileWatching(bool enable) {
|
||||
fileWatching_ = enable;
|
||||
if (enable) {
|
||||
|
|
@ -331,6 +399,11 @@ void ShaderSystem::setFileWatching(bool enable) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新文件监视
|
||||
*
|
||||
* 定期检查着色器文件是否发生变化,如有变化则自动重载
|
||||
*/
|
||||
void ShaderSystem::updateFileWatching() {
|
||||
if (!fileWatching_)
|
||||
return;
|
||||
|
|
@ -342,6 +415,11 @@ void ShaderSystem::updateFileWatching() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查并重载变化的着色器
|
||||
*
|
||||
* 检查所有着色器文件的修改时间,如有变化则自动重载
|
||||
*/
|
||||
void ShaderSystem::checkAndReload() {
|
||||
for (auto &[name, info] : shaders_) {
|
||||
if (info.isBuiltin)
|
||||
|
|
@ -359,6 +437,13 @@ void ShaderSystem::checkAndReload() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 重载指定着色器
|
||||
* @param name 着色器名称
|
||||
* @return 重载成功返回true,失败返回false
|
||||
*
|
||||
* 重新从文件加载并编译指定着色器
|
||||
*/
|
||||
bool ShaderSystem::reload(const std::string &name) {
|
||||
auto it = shaders_.find(name);
|
||||
if (it == shaders_.end()) {
|
||||
|
|
@ -387,6 +472,11 @@ bool ShaderSystem::reload(const std::string &name) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 重载所有着色器
|
||||
*
|
||||
* 重载所有非内置着色器
|
||||
*/
|
||||
void ShaderSystem::reloadAll() {
|
||||
for (const auto &[name, info] : shaders_) {
|
||||
if (!info.isBuiltin) {
|
||||
|
|
@ -395,22 +485,45 @@ void ShaderSystem::reloadAll() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取内置精灵着色器
|
||||
* @return 精灵着色器指针
|
||||
*/
|
||||
Ptr<GLShader> ShaderSystem::getBuiltinSpriteShader() {
|
||||
return builtinSpriteShader_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取内置粒子着色器
|
||||
* @return 粒子着色器指针
|
||||
*/
|
||||
Ptr<GLShader> ShaderSystem::getBuiltinParticleShader() {
|
||||
return builtinParticleShader_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取内置后处理着色器
|
||||
* @return 后处理着色器指针
|
||||
*/
|
||||
Ptr<GLShader> ShaderSystem::getBuiltinPostProcessShader() {
|
||||
return builtinPostProcessShader_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取内置形状着色器
|
||||
* @return 形状着色器指针
|
||||
*/
|
||||
Ptr<GLShader> ShaderSystem::getBuiltinShapeShader() {
|
||||
return builtinShapeShader_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读取文件内容
|
||||
* @param filepath 文件路径
|
||||
* @return 文件内容字符串,读取失败返回空字符串
|
||||
*
|
||||
* 以二进制模式读取文件全部内容
|
||||
*/
|
||||
std::string ShaderSystem::readFile(const std::string &filepath) {
|
||||
std::ifstream file(filepath, std::ios::in | std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
|
|
@ -422,6 +535,13 @@ std::string ShaderSystem::readFile(const std::string &filepath) {
|
|||
return buffer.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件修改时间
|
||||
* @param filepath 文件路径
|
||||
* @return 文件最后修改时间戳,失败返回0
|
||||
*
|
||||
* 获取文件的最后修改时间,用于文件监视功能
|
||||
*/
|
||||
uint64_t ShaderSystem::getFileModifiedTime(const std::string &filepath) {
|
||||
#ifdef _WIN32
|
||||
struct _stat64 statBuf;
|
||||
|
|
@ -441,47 +561,103 @@ uint64_t ShaderSystem::getFileModifiedTime(const std::string &filepath) {
|
|||
// ShaderParams实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param shader 关联的着色器对象
|
||||
*
|
||||
* 创建一个着色器参数设置器
|
||||
*/
|
||||
ShaderParams::ShaderParams(GLShader &shader) : shader_(shader) {}
|
||||
|
||||
/**
|
||||
* @brief 设置布尔类型uniform变量
|
||||
* @param name 变量名
|
||||
* @param value 布尔值
|
||||
* @return 当前对象引用,支持链式调用
|
||||
*/
|
||||
ShaderParams &ShaderParams::setBool(const std::string &name, bool value) {
|
||||
shader_.setBool(name, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置整数类型uniform变量
|
||||
* @param name 变量名
|
||||
* @param value 整数值
|
||||
* @return 当前对象引用,支持链式调用
|
||||
*/
|
||||
ShaderParams &ShaderParams::setInt(const std::string &name, int value) {
|
||||
shader_.setInt(name, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置浮点类型uniform变量
|
||||
* @param name 变量名
|
||||
* @param value 浮点值
|
||||
* @return 当前对象引用,支持链式调用
|
||||
*/
|
||||
ShaderParams &ShaderParams::setFloat(const std::string &name, float value) {
|
||||
shader_.setFloat(name, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置二维向量类型uniform变量
|
||||
* @param name 变量名
|
||||
* @param value 二维向量值
|
||||
* @return 当前对象引用,支持链式调用
|
||||
*/
|
||||
ShaderParams &ShaderParams::setVec2(const std::string &name,
|
||||
const glm::vec2 &value) {
|
||||
shader_.setVec2(name, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置三维向量类型uniform变量
|
||||
* @param name 变量名
|
||||
* @param value 三维向量值
|
||||
* @return 当前对象引用,支持链式调用
|
||||
*/
|
||||
ShaderParams &ShaderParams::setVec3(const std::string &name,
|
||||
const glm::vec3 &value) {
|
||||
shader_.setVec3(name, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置四维向量类型uniform变量
|
||||
* @param name 变量名
|
||||
* @param value 四维向量值
|
||||
* @return 当前对象引用,支持链式调用
|
||||
*/
|
||||
ShaderParams &ShaderParams::setVec4(const std::string &name,
|
||||
const glm::vec4 &value) {
|
||||
shader_.setVec4(name, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置4x4矩阵类型uniform变量
|
||||
* @param name 变量名
|
||||
* @param value 4x4矩阵值
|
||||
* @return 当前对象引用,支持链式调用
|
||||
*/
|
||||
ShaderParams &ShaderParams::setMat4(const std::string &name,
|
||||
const glm::mat4 &value) {
|
||||
shader_.setMat4(name, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置颜色类型uniform变量
|
||||
* @param name 变量名
|
||||
* @param color 颜色值
|
||||
* @return 当前对象引用,支持链式调用
|
||||
*
|
||||
* 将颜色对象转换为四维向量并设置
|
||||
*/
|
||||
ShaderParams &ShaderParams::setColor(const std::string &name,
|
||||
const Color &color) {
|
||||
shader_.setVec4(name, glm::vec4(color.r, color.g, color.b, color.a));
|
||||
|
|
|
|||
|
|
@ -9,6 +9,13 @@ namespace extra2d {
|
|||
// TextureAtlasPage 实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param width 页面宽度
|
||||
* @param height 页面高度
|
||||
*
|
||||
* 创建指定尺寸的纹理图集页面,初始化空白纹理和打包根节点
|
||||
*/
|
||||
TextureAtlasPage::TextureAtlasPage(int width, int height)
|
||||
: width_(width), height_(height), isFull_(false), usedArea_(0) {
|
||||
// 创建空白纹理
|
||||
|
|
@ -21,8 +28,24 @@ TextureAtlasPage::TextureAtlasPage(int width, int height)
|
|||
E2D_LOG_INFO("Created texture atlas page: {}x{}", width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 释放纹理图集页面资源
|
||||
*/
|
||||
TextureAtlasPage::~TextureAtlasPage() = default;
|
||||
|
||||
/**
|
||||
* @brief 尝试添加纹理到图集页面
|
||||
* @param name 纹理名称
|
||||
* @param texWidth 纹理宽度
|
||||
* @param texHeight 纹理高度
|
||||
* @param pixels 像素数据
|
||||
* @param[out] outUvRect 输出的UV坐标矩形
|
||||
* @return 添加成功返回true,失败返回false
|
||||
*
|
||||
* 尝试将纹理添加到图集页面中,使用矩形打包算法找到合适位置
|
||||
*/
|
||||
bool TextureAtlasPage::tryAddTexture(const std::string& name, int texWidth, int texHeight,
|
||||
const uint8_t* pixels, Rect& outUvRect) {
|
||||
if (isFull_) {
|
||||
|
|
@ -73,6 +96,15 @@ bool TextureAtlasPage::tryAddTexture(const std::string& name, int texWidth, int
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 插入纹理到打包树
|
||||
* @param node 当前节点
|
||||
* @param width 纹理宽度
|
||||
* @param height 纹理高度
|
||||
* @return 找到合适的节点返回节点指针,否则返回nullptr
|
||||
*
|
||||
* 使用二叉树算法递归查找合适的空间位置
|
||||
*/
|
||||
TextureAtlasPage::PackNode* TextureAtlasPage::insert(PackNode* node, int width, int height) {
|
||||
if (node == nullptr) {
|
||||
return nullptr;
|
||||
|
|
@ -116,6 +148,16 @@ TextureAtlasPage::PackNode* TextureAtlasPage::insert(PackNode* node, int width,
|
|||
return insert(node->left.get(), width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 写入像素数据到纹理
|
||||
* @param x 起始X坐标
|
||||
* @param y 起始Y坐标
|
||||
* @param w 宽度
|
||||
* @param h 高度
|
||||
* @param pixels 像素数据
|
||||
*
|
||||
* 使用glTexSubImage2D更新纹理的指定区域
|
||||
*/
|
||||
void TextureAtlasPage::writePixels(int x, int y, int w, int h, const uint8_t* pixels) {
|
||||
if (texture_ == nullptr || pixels == nullptr) {
|
||||
return;
|
||||
|
|
@ -130,6 +172,11 @@ void TextureAtlasPage::writePixels(int x, int y, int w, int h, const uint8_t* pi
|
|||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取图集中的纹理条目信息
|
||||
* @param name 纹理名称
|
||||
* @return 找到返回条目指针,未找到返回nullptr
|
||||
*/
|
||||
const AtlasEntry* TextureAtlasPage::getEntry(const std::string& name) const {
|
||||
auto it = entries_.find(name);
|
||||
if (it != entries_.end()) {
|
||||
|
|
@ -138,6 +185,12 @@ const AtlasEntry* TextureAtlasPage::getEntry(const std::string& name) const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取页面使用率
|
||||
* @return 使用率(0.0到1.0之间)
|
||||
*
|
||||
* 计算已使用面积占总面积的比例
|
||||
*/
|
||||
float TextureAtlasPage::getUsageRatio() const {
|
||||
return static_cast<float>(usedArea_) / (width_ * height_);
|
||||
}
|
||||
|
|
@ -146,6 +199,11 @@ float TextureAtlasPage::getUsageRatio() const {
|
|||
// TextureAtlas 实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 创建一个使用默认页面大小的纹理图集
|
||||
*/
|
||||
TextureAtlas::TextureAtlas()
|
||||
: pageSize_(TextureAtlasPage::DEFAULT_SIZE),
|
||||
sizeThreshold_(256),
|
||||
|
|
@ -153,14 +211,35 @@ TextureAtlas::TextureAtlas()
|
|||
initialized_(false) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 释放纹理图集资源
|
||||
*/
|
||||
TextureAtlas::~TextureAtlas() = default;
|
||||
|
||||
/**
|
||||
* @brief 初始化纹理图集
|
||||
* @param pageSize 页面大小
|
||||
*
|
||||
* 设置图集页面大小并标记为已初始化
|
||||
*/
|
||||
void TextureAtlas::init(int pageSize) {
|
||||
pageSize_ = pageSize;
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("TextureAtlas initialized with page size: {}", pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加纹理到图集
|
||||
* @param name 纹理名称
|
||||
* @param width 纹理宽度
|
||||
* @param height 纹理高度
|
||||
* @param pixels 像素数据
|
||||
* @return 添加成功返回true,失败返回false
|
||||
*
|
||||
* 尝试将纹理添加到现有页面,如空间不足则创建新页面
|
||||
*/
|
||||
bool TextureAtlas::addTexture(const std::string& name, int width, int height,
|
||||
const uint8_t* pixels) {
|
||||
if (!enabled_ || !initialized_) {
|
||||
|
|
@ -200,10 +279,20 @@ bool TextureAtlas::addTexture(const std::string& name, int width, int height,
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查纹理是否已存在于图集中
|
||||
* @param name 纹理名称
|
||||
* @return 存在返回true,不存在返回false
|
||||
*/
|
||||
bool TextureAtlas::contains(const std::string& name) const {
|
||||
return entryToPage_.find(name) != entryToPage_.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取纹理所在的图集纹理
|
||||
* @param name 纹理名称
|
||||
* @return 找到返回纹理指针,未找到返回nullptr
|
||||
*/
|
||||
const Texture* TextureAtlas::getAtlasTexture(const std::string& name) const {
|
||||
auto it = entryToPage_.find(name);
|
||||
if (it != entryToPage_.end()) {
|
||||
|
|
@ -212,6 +301,11 @@ const Texture* TextureAtlas::getAtlasTexture(const std::string& name) const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取纹理在图集中的UV坐标矩形
|
||||
* @param name 纹理名称
|
||||
* @return UV坐标矩形,未找到返回默认值
|
||||
*/
|
||||
Rect TextureAtlas::getUVRect(const std::string& name) const {
|
||||
auto it = entryToPage_.find(name);
|
||||
if (it != entryToPage_.end()) {
|
||||
|
|
@ -223,6 +317,11 @@ Rect TextureAtlas::getUVRect(const std::string& name) const {
|
|||
return Rect(0, 0, 1, 1); // 默认 UV
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取纹理的原始尺寸
|
||||
* @param name 纹理名称
|
||||
* @return 原始尺寸,未找到返回零向量
|
||||
*/
|
||||
Vec2 TextureAtlas::getOriginalSize(const std::string& name) const {
|
||||
auto it = entryToPage_.find(name);
|
||||
if (it != entryToPage_.end()) {
|
||||
|
|
@ -234,6 +333,12 @@ Vec2 TextureAtlas::getOriginalSize(const std::string& name) const {
|
|||
return Vec2(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取总使用率
|
||||
* @return 所有页面的平均使用率
|
||||
*
|
||||
* 计算所有页面的平均空间使用率
|
||||
*/
|
||||
float TextureAtlas::getTotalUsageRatio() const {
|
||||
if (pages_.empty()) {
|
||||
return 0.0f;
|
||||
|
|
@ -246,6 +351,11 @@ float TextureAtlas::getTotalUsageRatio() const {
|
|||
return total / pages_.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空图集
|
||||
*
|
||||
* 移除所有页面和条目映射
|
||||
*/
|
||||
void TextureAtlas::clear() {
|
||||
pages_.clear();
|
||||
entryToPage_.clear();
|
||||
|
|
@ -253,11 +363,17 @@ void TextureAtlas::clear() {
|
|||
}
|
||||
|
||||
// ============================================================================
|
||||
// TextureAtlasManager 单例实现
|
||||
// TextureAtlasMgr 单例实现
|
||||
// ============================================================================
|
||||
|
||||
TextureAtlasManager& TextureAtlasManager::getInstance() {
|
||||
static TextureAtlasManager instance;
|
||||
/**
|
||||
* @brief 获取TextureAtlasMgr单例实例
|
||||
* @return TextureAtlasMgr单例的引用
|
||||
*
|
||||
* 使用静态局部变量实现线程安全的单例模式
|
||||
*/
|
||||
TextureAtlasMgr& TextureAtlasMgr::get() {
|
||||
static TextureAtlasMgr instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ namespace extra2d {
|
|||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 创建一个未初始化的纹理池
|
||||
*/
|
||||
TexturePool::TexturePool()
|
||||
: scene_(nullptr)
|
||||
|
|
@ -27,6 +29,8 @@ TexturePool::TexturePool()
|
|||
* @brief 构造函数
|
||||
* @param scene 场景指针
|
||||
* @param maxMemoryUsage 最大内存使用量(0 表示无限制)
|
||||
*
|
||||
* 创建一个指定场景和内存限制的纹理池
|
||||
*/
|
||||
TexturePool::TexturePool(Scene* scene, size_t maxMemoryUsage)
|
||||
: scene_(scene)
|
||||
|
|
@ -42,6 +46,8 @@ TexturePool::TexturePool(Scene* scene, size_t maxMemoryUsage)
|
|||
* @brief 初始化纹理池
|
||||
* @param scene 场景指针
|
||||
* @param maxMemoryUsage 最大内存使用量(0 表示无限制)
|
||||
*
|
||||
* 设置纹理池的场景和内存限制
|
||||
*/
|
||||
void TexturePool::init(Scene* scene, size_t maxMemoryUsage) {
|
||||
scene_ = scene;
|
||||
|
|
@ -51,6 +57,8 @@ void TexturePool::init(Scene* scene, size_t maxMemoryUsage) {
|
|||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 清理纹理池并释放所有资源
|
||||
*/
|
||||
TexturePool::~TexturePool() {
|
||||
clear();
|
||||
|
|
@ -66,6 +74,8 @@ TexturePool::~TexturePool() {
|
|||
* @param path 文件路径
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*
|
||||
* 加载完整纹理文件到纹理池
|
||||
*/
|
||||
TextureRef TexturePool::load(const std::string& path, const TextureLoadOptions& options) {
|
||||
return load(path, Rect::Zero(), options);
|
||||
|
|
@ -77,6 +87,8 @@ TextureRef TexturePool::load(const std::string& path, const TextureLoadOptions&
|
|||
* @param region 纹理区域
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*
|
||||
* 加载纹理文件的指定区域到纹理池
|
||||
*/
|
||||
TextureRef TexturePool::load(const std::string& path, const Rect& region,
|
||||
const TextureLoadOptions& options) {
|
||||
|
|
@ -157,6 +169,8 @@ TextureRef TexturePool::load(const std::string& path, const Rect& region,
|
|||
* @param channels 通道数
|
||||
* @param key 缓存键
|
||||
* @return 纹理引用
|
||||
*
|
||||
* 从内存中的像素数据创建纹理并加入纹理池
|
||||
*/
|
||||
TextureRef TexturePool::loadFromMemory(const uint8_t* data, int width, int height,
|
||||
int channels, const std::string& key) {
|
||||
|
|
@ -226,6 +240,8 @@ TextureRef TexturePool::loadFromMemory(const uint8_t* data, int width, int heigh
|
|||
* @param path 文件路径
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*
|
||||
* 如果纹理已缓存则返回缓存,否则加载纹理
|
||||
*/
|
||||
TextureRef TexturePool::getOrLoad(const std::string& path, const TextureLoadOptions& options) {
|
||||
return getOrLoad(path, Rect::Zero(), options);
|
||||
|
|
@ -237,6 +253,8 @@ TextureRef TexturePool::getOrLoad(const std::string& path, const TextureLoadOpti
|
|||
* @param region 纹理区域
|
||||
* @param options 加载选项
|
||||
* @return 纹理引用
|
||||
*
|
||||
* 如果纹理区域已缓存则返回缓存,否则加载纹理区域
|
||||
*/
|
||||
TextureRef TexturePool::getOrLoad(const std::string& path, const Rect& region,
|
||||
const TextureLoadOptions& options) {
|
||||
|
|
@ -307,6 +325,8 @@ TextureRef TexturePool::getOrLoad(const std::string& path, const Rect& region,
|
|||
* @brief 增加引用计数
|
||||
* @param key 纹理键
|
||||
* @return 是否成功
|
||||
*
|
||||
* 增加指定纹理的引用计数
|
||||
*/
|
||||
bool TexturePool::addRef(const TextureKey& key) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
|
@ -324,6 +344,8 @@ bool TexturePool::addRef(const TextureKey& key) {
|
|||
* @brief 减少引用计数
|
||||
* @param key 纹理键
|
||||
* @return 减少后的引用计数
|
||||
*
|
||||
* 减少指定纹理的引用计数并返回新值
|
||||
*/
|
||||
uint32_t TexturePool::release(const TextureKey& key) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
|
@ -340,6 +362,8 @@ uint32_t TexturePool::release(const TextureKey& key) {
|
|||
* @brief 获取引用计数
|
||||
* @param key 纹理键
|
||||
* @return 引用计数
|
||||
*
|
||||
* 获取指定纹理的当前引用计数
|
||||
*/
|
||||
uint32_t TexturePool::getRefCount(const TextureKey& key) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
|
@ -359,6 +383,8 @@ uint32_t TexturePool::getRefCount(const TextureKey& key) const {
|
|||
* @brief 检查纹理是否已缓存
|
||||
* @param key 纹理键
|
||||
* @return 是否已缓存
|
||||
*
|
||||
* 检查指定纹理是否存在于缓存中
|
||||
*/
|
||||
bool TexturePool::isCached(const TextureKey& key) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
|
@ -369,6 +395,8 @@ bool TexturePool::isCached(const TextureKey& key) const {
|
|||
* @brief 从缓存中移除纹理
|
||||
* @param key 纹理键
|
||||
* @return 是否成功
|
||||
*
|
||||
* 从缓存中移除指定的纹理
|
||||
*/
|
||||
bool TexturePool::removeFromCache(const TextureKey& key) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
|
@ -386,6 +414,8 @@ bool TexturePool::removeFromCache(const TextureKey& key) {
|
|||
/**
|
||||
* @brief 垃圾回收(移除引用计数为 0 的纹理)
|
||||
* @return 移除的纹理数量
|
||||
*
|
||||
* 清理所有引用计数为0的纹理,释放内存
|
||||
*/
|
||||
size_t TexturePool::collectGarbage() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
|
@ -410,6 +440,8 @@ size_t TexturePool::collectGarbage() {
|
|||
|
||||
/**
|
||||
* @brief 清空所有缓存
|
||||
*
|
||||
* 移除纹理池中的所有纹理
|
||||
*/
|
||||
void TexturePool::clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
|
@ -427,6 +459,8 @@ void TexturePool::clear() {
|
|||
/**
|
||||
* @brief 获取当前内存使用量
|
||||
* @return 内存使用量(字节)
|
||||
*
|
||||
* 返回纹理池当前的内存使用量
|
||||
*/
|
||||
size_t TexturePool::getMemoryUsage() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
|
@ -436,6 +470,8 @@ size_t TexturePool::getMemoryUsage() const {
|
|||
/**
|
||||
* @brief 设置最大内存使用量
|
||||
* @param maxMemory 最大内存使用量(0 表示无限制)
|
||||
*
|
||||
* 设置纹理池的内存上限,如果当前使用量超过新上限则执行淘汰
|
||||
*/
|
||||
void TexturePool::setMaxMemoryUsage(size_t maxMemory) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
|
@ -453,6 +489,8 @@ void TexturePool::setMaxMemoryUsage(size_t maxMemory) {
|
|||
* @brief 执行 LRU 淘汰
|
||||
* @param targetMemory 目标内存使用量
|
||||
* @return 淘汰的纹理数量
|
||||
*
|
||||
* 根据LRU算法淘汰最少使用的纹理以达到目标内存使用量
|
||||
*/
|
||||
size_t TexturePool::evictLRU(size_t targetMemory) {
|
||||
// 注意:调用者应该已持有锁
|
||||
|
|
@ -506,7 +544,9 @@ size_t TexturePool::evictLRU(size_t targetMemory) {
|
|||
|
||||
/**
|
||||
* @brief 获取统计信息
|
||||
* @return 统计信息
|
||||
* @return 统计信息结构体
|
||||
*
|
||||
* 返回纹理池的统计信息,包括纹理数量、内存使用、缓存命中率等
|
||||
*/
|
||||
TexturePool::Stats TexturePool::getStats() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
|
@ -524,6 +564,8 @@ TexturePool::Stats TexturePool::getStats() const {
|
|||
|
||||
/**
|
||||
* @brief 重置统计信息
|
||||
*
|
||||
* 清零缓存命中、未命中和淘汰计数
|
||||
*/
|
||||
void TexturePool::resetStats() {
|
||||
cacheHits_.store(0, std::memory_order_relaxed);
|
||||
|
|
@ -539,6 +581,8 @@ void TexturePool::resetStats() {
|
|||
* @brief 计算纹理内存大小
|
||||
* @param texture 纹理对象
|
||||
* @return 内存大小(字节)
|
||||
*
|
||||
* 根据纹理的尺寸、通道数和像素格式计算内存占用
|
||||
*/
|
||||
size_t TexturePool::calculateTextureMemory(const Texture* texture) {
|
||||
if (!texture) {
|
||||
|
|
@ -587,6 +631,8 @@ size_t TexturePool::calculateTextureMemory(const Texture* texture) {
|
|||
/**
|
||||
* @brief 检查是否需要淘汰
|
||||
* @return 是否需要淘汰
|
||||
*
|
||||
* 检查当前内存使用量是否超过限制
|
||||
*/
|
||||
bool TexturePool::needsEviction() const {
|
||||
return maxMemoryUsage_ > 0 && currentMemoryUsage_ > maxMemoryUsage_;
|
||||
|
|
@ -594,6 +640,8 @@ bool TexturePool::needsEviction() const {
|
|||
|
||||
/**
|
||||
* @brief 尝试自动淘汰
|
||||
*
|
||||
* 如果内存使用量超过限制,执行LRU淘汰
|
||||
*/
|
||||
void TexturePool::tryAutoEvict() {
|
||||
if (needsEviction()) {
|
||||
|
|
|
|||
|
|
@ -4,38 +4,88 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 创建一个未配置的视口适配器
|
||||
*/
|
||||
ViewportAdapter::ViewportAdapter() = default;
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param logicWidth 逻辑宽度
|
||||
* @param logicHeight 逻辑高度
|
||||
*
|
||||
* 创建一个指定逻辑尺寸的视口适配器
|
||||
*/
|
||||
ViewportAdapter::ViewportAdapter(float logicWidth, float logicHeight) {
|
||||
config_.logicWidth = logicWidth;
|
||||
config_.logicHeight = logicHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视口配置
|
||||
* @param config 视口配置
|
||||
*
|
||||
* 设置完整的视口配置并标记矩阵为脏
|
||||
*/
|
||||
void ViewportAdapter::setConfig(const ViewportConfig &config) {
|
||||
config_ = config;
|
||||
matrixDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置逻辑尺寸
|
||||
* @param width 逻辑宽度
|
||||
* @param height 逻辑高度
|
||||
*
|
||||
* 设置视口的逻辑尺寸并标记矩阵为脏
|
||||
*/
|
||||
void ViewportAdapter::setLogicSize(float width, float height) {
|
||||
config_.logicWidth = width;
|
||||
config_.logicHeight = height;
|
||||
matrixDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视口模式
|
||||
* @param mode 视口模式
|
||||
*
|
||||
* 设置视口适配模式(宽高比、拉伸、居中等)
|
||||
*/
|
||||
void ViewportAdapter::setMode(ViewportMode mode) {
|
||||
config_.mode = mode;
|
||||
matrixDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置黑边位置
|
||||
* @param position 黑边位置
|
||||
*
|
||||
* 设置黑边相对于视口的位置
|
||||
*/
|
||||
void ViewportAdapter::setLetterboxPosition(LetterboxPosition position) {
|
||||
config_.letterboxPosition = position;
|
||||
matrixDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置黑边颜色
|
||||
* @param color 黑边颜色
|
||||
*
|
||||
* 设置黑边区域的填充颜色
|
||||
*/
|
||||
void ViewportAdapter::setLetterboxColor(const Color &color) {
|
||||
config_.letterboxColor = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新视口适配
|
||||
* @param screenWidth 屏幕宽度
|
||||
* @param screenHeight 屏幕高度
|
||||
*
|
||||
* 根据屏幕尺寸和配置计算视口参数
|
||||
*/
|
||||
void ViewportAdapter::update(int screenWidth, int screenHeight) {
|
||||
if (screenWidth_ == screenWidth && screenHeight_ == screenHeight &&
|
||||
!matrixDirty_) {
|
||||
|
|
@ -68,6 +118,11 @@ void ViewportAdapter::update(int screenWidth, int screenHeight) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算宽高比适配模式
|
||||
*
|
||||
* 保持逻辑宽高比,根据屏幕尺寸计算缩放和偏移
|
||||
*/
|
||||
void ViewportAdapter::calculateAspectRatio() {
|
||||
if (config_.logicHeight <= 0.0f || screenHeight_ <= 0) {
|
||||
result_ = ViewportResult();
|
||||
|
|
@ -104,6 +159,11 @@ void ViewportAdapter::calculateAspectRatio() {
|
|||
calculateLetterbox();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算拉伸模式
|
||||
*
|
||||
* 拉伸逻辑视口以填满整个屏幕
|
||||
*/
|
||||
void ViewportAdapter::calculateStretch() {
|
||||
result_.scaleX = static_cast<float>(screenWidth_) / config_.logicWidth;
|
||||
result_.scaleY = static_cast<float>(screenHeight_) / config_.logicHeight;
|
||||
|
|
@ -117,6 +177,11 @@ void ViewportAdapter::calculateStretch() {
|
|||
result_.hasLetterbox = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算居中模式
|
||||
*
|
||||
* 将逻辑视口居中显示,可选自动缩放
|
||||
*/
|
||||
void ViewportAdapter::calculateCenter() {
|
||||
float displayWidth = config_.logicWidth;
|
||||
float displayHeight = config_.logicHeight;
|
||||
|
|
@ -155,6 +220,11 @@ void ViewportAdapter::calculateCenter() {
|
|||
calculateLetterbox();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算自定义模式
|
||||
*
|
||||
* 使用自定义缩放和偏移参数
|
||||
*/
|
||||
void ViewportAdapter::calculateCustom() {
|
||||
result_.scaleX = config_.customScale;
|
||||
result_.scaleY = config_.customScale;
|
||||
|
|
@ -176,6 +246,11 @@ void ViewportAdapter::calculateCustom() {
|
|||
calculateLetterbox();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算黑边区域
|
||||
*
|
||||
* 根据视口偏移计算上下左右黑边矩形
|
||||
*/
|
||||
void ViewportAdapter::calculateLetterbox() {
|
||||
result_.hasLetterbox = false;
|
||||
|
||||
|
|
@ -201,6 +276,13 @@ void ViewportAdapter::calculateLetterbox() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 应用黑边位置
|
||||
* @param extraWidth 额外宽度
|
||||
* @param extraHeight 额外高度
|
||||
*
|
||||
* 根据配置调整视口偏移以实现不同的黑边位置
|
||||
*/
|
||||
void ViewportAdapter::applyLetterboxPosition(float extraWidth,
|
||||
float extraHeight) {
|
||||
if (extraWidth <= 0.0f && extraHeight <= 0.0f) {
|
||||
|
|
@ -251,25 +333,57 @@ void ViewportAdapter::applyLetterboxPosition(float extraWidth,
|
|||
result_.viewport.origin = result_.offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将屏幕坐标转换为逻辑坐标
|
||||
* @param screenPos 屏幕坐标
|
||||
* @return 逻辑坐标
|
||||
*
|
||||
* 根据当前缩放和偏移计算对应的逻辑坐标
|
||||
*/
|
||||
Vec2 ViewportAdapter::screenToLogic(const Vec2 &screenPos) const {
|
||||
return Vec2((screenPos.x - result_.offset.x) / result_.scaleX,
|
||||
(screenPos.y - result_.offset.y) / result_.scaleY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将逻辑坐标转换为屏幕坐标
|
||||
* @param logicPos 逻辑坐标
|
||||
* @return 屏幕坐标
|
||||
*
|
||||
* 根据当前缩放和偏移计算对应的屏幕坐标
|
||||
*/
|
||||
Vec2 ViewportAdapter::logicToScreen(const Vec2 &logicPos) const {
|
||||
return Vec2(logicPos.x * result_.scaleX + result_.offset.x,
|
||||
logicPos.y * result_.scaleY + result_.offset.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将屏幕坐标转换为逻辑坐标
|
||||
* @param x 屏幕X坐标
|
||||
* @param y 屏幕Y坐标
|
||||
* @return 逻辑坐标
|
||||
*/
|
||||
Vec2 ViewportAdapter::screenToLogic(float x, float y) const {
|
||||
return screenToLogic(Vec2(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将逻辑坐标转换为屏幕坐标
|
||||
* @param x 逻辑X坐标
|
||||
* @param y 逻辑Y坐标
|
||||
* @return 屏幕坐标
|
||||
*/
|
||||
Vec2 ViewportAdapter::logicToScreen(float x, float y) const {
|
||||
return logicToScreen(Vec2(x, y));
|
||||
}
|
||||
|
||||
glm::mat4 ViewportAdapter::getViewportMatrix() const {
|
||||
/**
|
||||
* @brief 获取视口变换矩阵
|
||||
* @return 4x4变换矩阵
|
||||
*
|
||||
* 返回用于将逻辑坐标转换为屏幕坐标的变换矩阵
|
||||
*/
|
||||
glm::mat4 ViewportAdapter::getMatrix() const {
|
||||
if (matrixDirty_) {
|
||||
viewportMatrix_ = glm::mat4(1.0f);
|
||||
viewportMatrix_ = glm::translate(viewportMatrix_,
|
||||
|
|
@ -281,18 +395,34 @@ glm::mat4 ViewportAdapter::getViewportMatrix() const {
|
|||
return viewportMatrix_;
|
||||
}
|
||||
|
||||
glm::mat4 ViewportAdapter::getInverseViewportMatrix() const {
|
||||
/**
|
||||
* @brief 获取视口逆变换矩阵
|
||||
* @return 4x4逆变换矩阵
|
||||
*
|
||||
* 返回用于将屏幕坐标转换为逻辑坐标的逆变换矩阵
|
||||
*/
|
||||
glm::mat4 ViewportAdapter::getInvMatrix() const {
|
||||
if (matrixDirty_) {
|
||||
getViewportMatrix();
|
||||
getMatrix();
|
||||
}
|
||||
inverseViewportMatrix_ = glm::inverse(viewportMatrix_);
|
||||
return inverseViewportMatrix_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查屏幕坐标是否在视口内
|
||||
* @param screenPos 屏幕坐标
|
||||
* @return 在视口内返回true,否则返回false
|
||||
*/
|
||||
bool ViewportAdapter::isInViewport(const Vec2 &screenPos) const {
|
||||
return result_.viewport.containsPoint(screenPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查屏幕坐标是否在黑边区域内
|
||||
* @param screenPos 屏幕坐标
|
||||
* @return 在黑边区域内返回true,否则返回false
|
||||
*/
|
||||
bool ViewportAdapter::isInLetterbox(const Vec2 &screenPos) const {
|
||||
if (!result_.hasLetterbox) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -7,17 +7,34 @@ namespace extra2d {
|
|||
// Switch 推荐 VRAM 预算 ~400MB
|
||||
static constexpr size_t DEFAULT_VRAM_BUDGET = 400 * 1024 * 1024;
|
||||
|
||||
VRAMManager::VRAMManager()
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 初始化VRAM管理器,设置默认预算为400MB
|
||||
*/
|
||||
VRAMMgr::VRAMMgr()
|
||||
: textureVRAM_(0), bufferVRAM_(0), vramBudget_(DEFAULT_VRAM_BUDGET),
|
||||
textureAllocCount_(0), textureFreeCount_(0), bufferAllocCount_(0),
|
||||
bufferFreeCount_(0), peakTextureVRAM_(0), peakBufferVRAM_(0) {}
|
||||
|
||||
VRAMManager &VRAMManager::getInstance() {
|
||||
static VRAMManager instance;
|
||||
/**
|
||||
* @brief 获取VRAMMgr单例实例
|
||||
* @return VRAMMgr单例的引用
|
||||
*
|
||||
* 使用静态局部变量实现线程安全的单例模式
|
||||
*/
|
||||
VRAMMgr &VRAMMgr::get() {
|
||||
static VRAMMgr instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void VRAMManager::allocTexture(size_t size) {
|
||||
/**
|
||||
* @brief 分配纹理VRAM
|
||||
* @param size 分配的字节数
|
||||
*
|
||||
* 增加纹理VRAM使用量并更新峰值,如果超出预算则输出警告
|
||||
*/
|
||||
void VRAMMgr::allocTexture(size_t size) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
textureVRAM_ += size;
|
||||
textureAllocCount_++;
|
||||
|
|
@ -29,7 +46,13 @@ void VRAMManager::allocTexture(size_t size) {
|
|||
}
|
||||
}
|
||||
|
||||
void VRAMManager::freeTexture(size_t size) {
|
||||
/**
|
||||
* @brief 释放纹理VRAM
|
||||
* @param size 释放的字节数
|
||||
*
|
||||
* 减少纹理VRAM使用量
|
||||
*/
|
||||
void VRAMMgr::freeTexture(size_t size) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (size <= textureVRAM_) {
|
||||
textureVRAM_ -= size;
|
||||
|
|
@ -39,7 +62,13 @@ void VRAMManager::freeTexture(size_t size) {
|
|||
textureFreeCount_++;
|
||||
}
|
||||
|
||||
void VRAMManager::allocBuffer(size_t size) {
|
||||
/**
|
||||
* @brief 分配缓冲VRAM
|
||||
* @param size 分配的字节数
|
||||
*
|
||||
* 增加缓冲VRAM使用量并更新峰值,如果超出预算则输出警告
|
||||
*/
|
||||
void VRAMMgr::allocBuffer(size_t size) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
bufferVRAM_ += size;
|
||||
bufferAllocCount_++;
|
||||
|
|
@ -51,7 +80,13 @@ void VRAMManager::allocBuffer(size_t size) {
|
|||
}
|
||||
}
|
||||
|
||||
void VRAMManager::freeBuffer(size_t size) {
|
||||
/**
|
||||
* @brief 释放缓冲VRAM
|
||||
* @param size 释放的字节数
|
||||
*
|
||||
* 减少缓冲VRAM使用量
|
||||
*/
|
||||
void VRAMMgr::freeBuffer(size_t size) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (size <= bufferVRAM_) {
|
||||
bufferVRAM_ -= size;
|
||||
|
|
@ -61,28 +96,67 @@ void VRAMManager::freeBuffer(size_t size) {
|
|||
bufferFreeCount_++;
|
||||
}
|
||||
|
||||
size_t VRAMManager::getUsedVRAM() const { return textureVRAM_ + bufferVRAM_; }
|
||||
/**
|
||||
* @brief 获取总VRAM使用量
|
||||
* @return 总使用字节数
|
||||
*
|
||||
* 返回纹理和缓冲VRAM使用量的总和
|
||||
*/
|
||||
size_t VRAMMgr::getUsedVRAM() const { return textureVRAM_ + bufferVRAM_; }
|
||||
|
||||
size_t VRAMManager::getTextureVRAM() const { return textureVRAM_; }
|
||||
/**
|
||||
* @brief 获取纹理VRAM使用量
|
||||
* @return 纹理使用字节数
|
||||
*/
|
||||
size_t VRAMMgr::getTextureVRAM() const { return textureVRAM_; }
|
||||
|
||||
size_t VRAMManager::getBufferVRAM() const { return bufferVRAM_; }
|
||||
/**
|
||||
* @brief 获取缓冲VRAM使用量
|
||||
* @return 缓冲使用字节数
|
||||
*/
|
||||
size_t VRAMMgr::getBufferVRAM() const { return bufferVRAM_; }
|
||||
|
||||
size_t VRAMManager::getAvailableVRAM() const {
|
||||
/**
|
||||
* @brief 获取可用VRAM
|
||||
* @return 可用字节数
|
||||
*
|
||||
* 返回预算内剩余的VRAM空间
|
||||
*/
|
||||
size_t VRAMMgr::getAvailableVRAM() const {
|
||||
size_t used = getUsedVRAM();
|
||||
return (used < vramBudget_) ? (vramBudget_ - used) : 0;
|
||||
}
|
||||
|
||||
void VRAMManager::setVRAMBudget(size_t budget) {
|
||||
/**
|
||||
* @brief 设置VRAM预算
|
||||
* @param budget 预算字节数
|
||||
*
|
||||
* 设置VRAM使用上限
|
||||
*/
|
||||
void VRAMMgr::setVRAMBudget(size_t budget) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
vramBudget_ = budget;
|
||||
E2D_LOG_INFO("VRAM budget set to {} MB", budget / (1024 * 1024));
|
||||
}
|
||||
|
||||
size_t VRAMManager::getVRAMBudget() const { return vramBudget_; }
|
||||
/**
|
||||
* @brief 获取VRAM预算
|
||||
* @return 预算字节数
|
||||
*/
|
||||
size_t VRAMMgr::getVRAMBudget() const { return vramBudget_; }
|
||||
|
||||
bool VRAMManager::isOverBudget() const { return getUsedVRAM() > vramBudget_; }
|
||||
/**
|
||||
* @brief 检查是否超出预算
|
||||
* @return 超出预算返回true,否则返回false
|
||||
*/
|
||||
bool VRAMMgr::isOverBudget() const { return getUsedVRAM() > vramBudget_; }
|
||||
|
||||
void VRAMManager::printStats() const {
|
||||
/**
|
||||
* @brief 打印VRAM统计信息
|
||||
*
|
||||
* 输出纹理和缓冲的VRAM使用情况、峰值和分配/释放次数
|
||||
*/
|
||||
void VRAMMgr::printStats() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
E2D_LOG_INFO("=== VRAM Stats ===");
|
||||
E2D_LOG_INFO(" Texture VRAM: {} MB (peak: {} MB)",
|
||||
|
|
@ -98,7 +172,12 @@ void VRAMManager::printStats() const {
|
|||
bufferFreeCount_);
|
||||
}
|
||||
|
||||
void VRAMManager::reset() {
|
||||
/**
|
||||
* @brief 重置所有统计信息
|
||||
*
|
||||
* 清零所有VRAM计数器和峰值记录
|
||||
*/
|
||||
void VRAMMgr::reset() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
textureVRAM_ = 0;
|
||||
bufferVRAM_ = 0;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 初始化输入系统的所有成员变量为默认值,包括控制器指针、摇杆状态、
|
||||
* 鼠标状态、触摸状态等,并将所有按键和按钮状态数组初始化为false。
|
||||
*/
|
||||
Input::Input()
|
||||
: controller_(nullptr),
|
||||
leftStickX_(0.0f), leftStickY_(0.0f),
|
||||
|
|
@ -13,7 +19,6 @@ Input::Input()
|
|||
touching_(false), prevTouching_(false), touchCount_(0),
|
||||
viewportAdapter_(nullptr) {
|
||||
|
||||
// 初始化所有状态数组
|
||||
keysDown_.fill(false);
|
||||
prevKeysDown_.fill(false);
|
||||
buttonsDown_.fill(false);
|
||||
|
|
@ -22,10 +27,19 @@ Input::Input()
|
|||
prevMouseButtonsDown_.fill(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 自动调用shutdown()方法释放所有资源。
|
||||
*/
|
||||
Input::~Input() { shutdown(); }
|
||||
|
||||
/**
|
||||
* @brief 初始化输入系统
|
||||
*
|
||||
* 打开第一个可用的游戏控制器,并在PC端获取初始鼠标位置。
|
||||
*/
|
||||
void Input::init() {
|
||||
// 打开第一个可用的游戏控制器
|
||||
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
|
||||
if (SDL_IsGameController(i)) {
|
||||
controller_ = SDL_GameControllerOpen(i);
|
||||
|
|
@ -41,7 +55,6 @@ void Input::init() {
|
|||
E2D_LOG_WARN("No game controller found");
|
||||
}
|
||||
|
||||
// PC 端获取初始鼠标状态
|
||||
#ifndef PLATFORM_SWITCH
|
||||
int mouseX, mouseY;
|
||||
SDL_GetMouseState(&mouseX, &mouseY);
|
||||
|
|
@ -50,6 +63,11 @@ void Input::init() {
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 关闭输入系统
|
||||
*
|
||||
* 关闭并释放游戏控制器资源。
|
||||
*/
|
||||
void Input::shutdown() {
|
||||
if (controller_) {
|
||||
SDL_GameControllerClose(controller_);
|
||||
|
|
@ -57,8 +75,12 @@ void Input::shutdown() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新输入状态
|
||||
*
|
||||
* 保存上一帧的输入状态,并更新键盘、鼠标、手柄和触摸设备的当前状态。
|
||||
*/
|
||||
void Input::update() {
|
||||
// 保存上一帧状态
|
||||
prevKeysDown_ = keysDown_;
|
||||
prevButtonsDown_ = buttonsDown_;
|
||||
prevMouseButtonsDown_ = mouseButtonsDown_;
|
||||
|
|
@ -67,50 +89,56 @@ void Input::update() {
|
|||
prevTouching_ = touching_;
|
||||
prevTouchPosition_ = touchPosition_;
|
||||
|
||||
// 更新各输入设备状态
|
||||
updateKeyboard();
|
||||
updateMouse();
|
||||
updateGamepad();
|
||||
updateTouch();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新键盘状态
|
||||
*
|
||||
* 从SDL获取当前键盘状态并更新按键数组。
|
||||
*/
|
||||
void Input::updateKeyboard() {
|
||||
// 获取当前键盘状态
|
||||
const Uint8* state = SDL_GetKeyboardState(nullptr);
|
||||
for (int i = 0; i < MAX_KEYS; ++i) {
|
||||
keysDown_[i] = state[i] != 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新鼠标状态
|
||||
*
|
||||
* 获取当前鼠标位置和按钮状态。仅在非Switch平台执行。
|
||||
*/
|
||||
void Input::updateMouse() {
|
||||
#ifndef PLATFORM_SWITCH
|
||||
// 更新鼠标位置
|
||||
int mouseX, mouseY;
|
||||
Uint32 buttonState = SDL_GetMouseState(&mouseX, &mouseY);
|
||||
mousePosition_ = Vec2(static_cast<float>(mouseX), static_cast<float>(mouseY));
|
||||
|
||||
// 更新鼠标按钮状态
|
||||
mouseButtonsDown_[0] = (buttonState & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0;
|
||||
mouseButtonsDown_[1] = (buttonState & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
|
||||
mouseButtonsDown_[2] = (buttonState & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0;
|
||||
mouseButtonsDown_[3] = (buttonState & SDL_BUTTON(SDL_BUTTON_X1)) != 0;
|
||||
mouseButtonsDown_[4] = (buttonState & SDL_BUTTON(SDL_BUTTON_X2)) != 0;
|
||||
|
||||
// 处理鼠标滚轮事件(需要在事件循环中处理,这里简化处理)
|
||||
// 实际滚轮值通过 SDL_MOUSEWHEEL 事件更新
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新手柄状态
|
||||
*
|
||||
* 读取手柄按钮状态和摇杆位置,摇杆值归一化到-1.0~1.0范围。
|
||||
*/
|
||||
void Input::updateGamepad() {
|
||||
if (controller_) {
|
||||
// 更新按钮状态
|
||||
for (int i = 0; i < MAX_BUTTONS; ++i) {
|
||||
buttonsDown_[i] =
|
||||
SDL_GameControllerGetButton(
|
||||
controller_, static_cast<SDL_GameControllerButton>(i)) != 0;
|
||||
}
|
||||
|
||||
// 读取摇杆(归一化到 -1.0 ~ 1.0)
|
||||
leftStickX_ = static_cast<float>(SDL_GameControllerGetAxis(
|
||||
controller_, SDL_CONTROLLER_AXIS_LEFTX)) / 32767.0f;
|
||||
leftStickY_ = static_cast<float>(SDL_GameControllerGetAxis(
|
||||
|
|
@ -125,9 +153,14 @@ void Input::updateGamepad() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新触摸状态
|
||||
*
|
||||
* 获取触摸设备状态,将归一化坐标转换为像素坐标。
|
||||
* Switch平台使用原生触摸屏支持,PC端支持可选触摸设备。
|
||||
*/
|
||||
void Input::updateTouch() {
|
||||
#ifdef PLATFORM_SWITCH
|
||||
// Switch 原生触摸屏支持
|
||||
SDL_TouchID touchId = SDL_GetTouchDevice(0);
|
||||
if (touchId != 0) {
|
||||
touchCount_ = SDL_GetNumTouchFingers(touchId);
|
||||
|
|
@ -135,7 +168,6 @@ void Input::updateTouch() {
|
|||
SDL_Finger *finger = SDL_GetTouchFinger(touchId, 0);
|
||||
if (finger) {
|
||||
touching_ = true;
|
||||
// SDL 触摸坐标是归一化 0.0~1.0,转换为像素坐标
|
||||
touchPosition_ = Vec2(finger->x * 1280.0f, finger->y * 720.0f);
|
||||
} else {
|
||||
touching_ = false;
|
||||
|
|
@ -148,7 +180,6 @@ void Input::updateTouch() {
|
|||
touching_ = false;
|
||||
}
|
||||
#else
|
||||
// PC 端:触摸屏可选支持(如果有触摸设备)
|
||||
SDL_TouchID touchId = SDL_GetTouchDevice(0);
|
||||
if (touchId != 0) {
|
||||
touchCount_ = SDL_GetNumTouchFingers(touchId);
|
||||
|
|
@ -156,7 +187,6 @@ void Input::updateTouch() {
|
|||
SDL_Finger *finger = SDL_GetTouchFinger(touchId, 0);
|
||||
if (finger) {
|
||||
touching_ = true;
|
||||
// PC 端需要根据窗口大小转换坐标
|
||||
int windowWidth, windowHeight;
|
||||
SDL_Window* window = SDL_GL_GetCurrentWindow();
|
||||
if (window) {
|
||||
|
|
@ -182,9 +212,16 @@ void Input::updateTouch() {
|
|||
// 键盘输入
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 将键盘按键映射到手柄按钮
|
||||
*
|
||||
* 提供键盘到手柄按钮的映射,用于在Switch平台模拟键盘输入。
|
||||
*
|
||||
* @param keyCode 键盘按键码
|
||||
* @return 对应的SDL手柄按钮枚举值
|
||||
*/
|
||||
SDL_GameControllerButton Input::mapKeyToButton(int keyCode) const {
|
||||
switch (keyCode) {
|
||||
// 方向键 → DPad
|
||||
case Key::Up:
|
||||
return SDL_CONTROLLER_BUTTON_DPAD_UP;
|
||||
case Key::Down:
|
||||
|
|
@ -194,7 +231,6 @@ SDL_GameControllerButton Input::mapKeyToButton(int keyCode) const {
|
|||
case Key::Right:
|
||||
return SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
|
||||
|
||||
// WASD → 也映射到 DPad
|
||||
case Key::W:
|
||||
return SDL_CONTROLLER_BUTTON_DPAD_UP;
|
||||
case Key::S:
|
||||
|
|
@ -204,7 +240,6 @@ SDL_GameControllerButton Input::mapKeyToButton(int keyCode) const {
|
|||
case Key::D:
|
||||
return SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
|
||||
|
||||
// 常用键 → 手柄按钮
|
||||
case Key::Z:
|
||||
return SDL_CONTROLLER_BUTTON_B;
|
||||
case Key::X:
|
||||
|
|
@ -220,13 +255,11 @@ SDL_GameControllerButton Input::mapKeyToButton(int keyCode) const {
|
|||
case Key::Escape:
|
||||
return SDL_CONTROLLER_BUTTON_START;
|
||||
|
||||
// 肩键
|
||||
case Key::Q:
|
||||
return SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
|
||||
case Key::E:
|
||||
return SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
|
||||
|
||||
// Start/Select
|
||||
case Key::Tab:
|
||||
return SDL_CONTROLLER_BUTTON_BACK;
|
||||
case Key::Backspace:
|
||||
|
|
@ -237,15 +270,22 @@ SDL_GameControllerButton Input::mapKeyToButton(int keyCode) const {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查按键是否被按下
|
||||
*
|
||||
* 检测指定按键当前是否处于按下状态。
|
||||
* Switch平台映射到手柄按钮,PC端直接读取键盘状态。
|
||||
*
|
||||
* @param keyCode 键盘按键码
|
||||
* @return 按键按下返回true,否则返回false
|
||||
*/
|
||||
bool Input::isKeyDown(int keyCode) const {
|
||||
#ifdef PLATFORM_SWITCH
|
||||
// Switch: 映射到手柄按钮
|
||||
SDL_GameControllerButton button = mapKeyToButton(keyCode);
|
||||
if (button == SDL_CONTROLLER_BUTTON_INVALID)
|
||||
return false;
|
||||
return buttonsDown_[button];
|
||||
#else
|
||||
// PC: 直接使用键盘扫描码
|
||||
SDL_Scancode scancode = SDL_GetScancodeFromKey(keyCode);
|
||||
if (scancode >= 0 && scancode < MAX_KEYS) {
|
||||
return keysDown_[scancode];
|
||||
|
|
@ -254,6 +294,14 @@ bool Input::isKeyDown(int keyCode) const {
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查按键是否刚被按下
|
||||
*
|
||||
* 检测指定按键是否在本帧刚被按下(之前未按下,当前按下)。
|
||||
*
|
||||
* @param keyCode 键盘按键码
|
||||
* @return 按键刚按下返回true,否则返回false
|
||||
*/
|
||||
bool Input::isKeyPressed(int keyCode) const {
|
||||
#ifdef PLATFORM_SWITCH
|
||||
SDL_GameControllerButton button = mapKeyToButton(keyCode);
|
||||
|
|
@ -269,6 +317,14 @@ bool Input::isKeyPressed(int keyCode) const {
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查按键是否刚被释放
|
||||
*
|
||||
* 检测指定按键是否在本帧刚被释放(之前按下,当前未按下)。
|
||||
*
|
||||
* @param keyCode 键盘按键码
|
||||
* @return 按键刚释放返回true,否则返回false
|
||||
*/
|
||||
bool Input::isKeyReleased(int keyCode) const {
|
||||
#ifdef PLATFORM_SWITCH
|
||||
SDL_GameControllerButton button = mapKeyToButton(keyCode);
|
||||
|
|
@ -288,39 +344,74 @@ bool Input::isKeyReleased(int keyCode) const {
|
|||
// 手柄按钮
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 检查手柄按钮是否被按下
|
||||
*
|
||||
* @param button 手柄按钮索引
|
||||
* @return 按钮按下返回true,否则返回false
|
||||
*/
|
||||
bool Input::isButtonDown(int button) const {
|
||||
if (button < 0 || button >= MAX_BUTTONS)
|
||||
return false;
|
||||
return buttonsDown_[button];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查手柄按钮是否刚被按下
|
||||
*
|
||||
* @param button 手柄按钮索引
|
||||
* @return 按钮刚按下返回true,否则返回false
|
||||
*/
|
||||
bool Input::isButtonPressed(int button) const {
|
||||
if (button < 0 || button >= MAX_BUTTONS)
|
||||
return false;
|
||||
return buttonsDown_[button] && !prevButtonsDown_[button];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查手柄按钮是否刚被释放
|
||||
*
|
||||
* @param button 手柄按钮索引
|
||||
* @return 按钮刚释放返回true,否则返回false
|
||||
*/
|
||||
bool Input::isButtonReleased(int button) const {
|
||||
if (button < 0 || button >= MAX_BUTTONS)
|
||||
return false;
|
||||
return !buttonsDown_[button] && prevButtonsDown_[button];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取左摇杆位置
|
||||
*
|
||||
* @return 包含X和Y轴值的二维向量,范围-1.0~1.0
|
||||
*/
|
||||
Vec2 Input::getLeftStick() const { return Vec2(leftStickX_, leftStickY_); }
|
||||
|
||||
/**
|
||||
* @brief 获取右摇杆位置
|
||||
*
|
||||
* @return 包含X和Y轴值的二维向量,范围-1.0~1.0
|
||||
*/
|
||||
Vec2 Input::getRightStick() const { return Vec2(rightStickX_, rightStickY_); }
|
||||
|
||||
// ============================================================================
|
||||
// 鼠标输入
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 检查鼠标按钮是否被按下
|
||||
*
|
||||
* Switch平台左键映射到触摸,右键映射到A键。
|
||||
*
|
||||
* @param button 鼠标按钮枚举值
|
||||
* @return 按钮按下返回true,否则返回false
|
||||
*/
|
||||
bool Input::isMouseDown(MouseButton button) const {
|
||||
int index = static_cast<int>(button);
|
||||
if (index < 0 || index >= 8)
|
||||
return false;
|
||||
|
||||
#ifdef PLATFORM_SWITCH
|
||||
// Switch: 左键映射到触摸,右键映射到 A 键
|
||||
if (button == MouseButton::Left) {
|
||||
return touching_;
|
||||
}
|
||||
|
|
@ -329,11 +420,16 @@ bool Input::isMouseDown(MouseButton button) const {
|
|||
}
|
||||
return false;
|
||||
#else
|
||||
// PC: 直接使用鼠标按钮
|
||||
return mouseButtonsDown_[index];
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查鼠标按钮是否刚被按下
|
||||
*
|
||||
* @param button 鼠标按钮枚举值
|
||||
* @return 按钮刚按下返回true,否则返回false
|
||||
*/
|
||||
bool Input::isMousePressed(MouseButton button) const {
|
||||
int index = static_cast<int>(button);
|
||||
if (index < 0 || index >= 8)
|
||||
|
|
@ -353,6 +449,12 @@ bool Input::isMousePressed(MouseButton button) const {
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查鼠标按钮是否刚被释放
|
||||
*
|
||||
* @param button 鼠标按钮枚举值
|
||||
* @return 按钮刚释放返回true,否则返回false
|
||||
*/
|
||||
bool Input::isMouseReleased(MouseButton button) const {
|
||||
int index = static_cast<int>(button);
|
||||
if (index < 0 || index >= 8)
|
||||
|
|
@ -372,6 +474,13 @@ bool Input::isMouseReleased(MouseButton button) const {
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取鼠标位置
|
||||
*
|
||||
* Switch平台返回触摸位置,PC端返回鼠标位置。
|
||||
*
|
||||
* @return 包含X和Y坐标的二维向量(屏幕像素坐标)
|
||||
*/
|
||||
Vec2 Input::getMousePosition() const {
|
||||
#ifdef PLATFORM_SWITCH
|
||||
return touchPosition_;
|
||||
|
|
@ -380,6 +489,13 @@ Vec2 Input::getMousePosition() const {
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取鼠标移动增量
|
||||
*
|
||||
* 计算当前帧与上一帧之间的鼠标位置差值。
|
||||
*
|
||||
* @return 包含X和Y移动量的二维向量
|
||||
*/
|
||||
Vec2 Input::getMouseDelta() const {
|
||||
#ifdef PLATFORM_SWITCH
|
||||
if (touching_ && prevTouching_) {
|
||||
|
|
@ -391,6 +507,13 @@ Vec2 Input::getMouseDelta() const {
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置鼠标位置
|
||||
*
|
||||
* 将鼠标移动到指定的屏幕坐标位置。仅在PC端有效。
|
||||
*
|
||||
* @param position 目标位置(屏幕像素坐标)
|
||||
*/
|
||||
void Input::setMousePosition(const Vec2 &position) {
|
||||
#ifndef PLATFORM_SWITCH
|
||||
SDL_WarpMouseInWindow(SDL_GL_GetCurrentWindow(),
|
||||
|
|
@ -401,6 +524,13 @@ void Input::setMousePosition(const Vec2 &position) {
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置鼠标光标可见性
|
||||
*
|
||||
* 显示或隐藏鼠标光标。仅在PC端有效。
|
||||
*
|
||||
* @param visible true显示光标,false隐藏光标
|
||||
*/
|
||||
void Input::setMouseVisible(bool visible) {
|
||||
#ifndef PLATFORM_SWITCH
|
||||
SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
|
||||
|
|
@ -409,6 +539,13 @@ void Input::setMouseVisible(bool visible) {
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置鼠标锁定模式
|
||||
*
|
||||
* 启用或禁用相对鼠标模式,用于第一人称视角控制等场景。
|
||||
*
|
||||
* @param locked true锁定鼠标到窗口中心,false解锁
|
||||
*/
|
||||
void Input::setMouseLocked(bool locked) {
|
||||
#ifndef PLATFORM_SWITCH
|
||||
SDL_SetRelativeMouseMode(locked ? SDL_TRUE : SDL_FALSE);
|
||||
|
|
@ -421,6 +558,13 @@ void Input::setMouseLocked(bool locked) {
|
|||
// 便捷方法
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 检查是否有任意按键被按下
|
||||
*
|
||||
* Switch平台检查手柄按钮,PC端检查键盘按键。
|
||||
*
|
||||
* @return 有按键按下返回true,否则返回false
|
||||
*/
|
||||
bool Input::isAnyKeyDown() const {
|
||||
#ifdef PLATFORM_SWITCH
|
||||
for (int i = 0; i < MAX_BUTTONS; ++i) {
|
||||
|
|
@ -436,6 +580,13 @@ bool Input::isAnyKeyDown() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查是否有任意鼠标按钮被按下
|
||||
*
|
||||
* Switch平台检查触摸状态,PC端检查鼠标按钮。
|
||||
*
|
||||
* @return 有按钮按下返回true,否则返回false
|
||||
*/
|
||||
bool Input::isAnyMouseDown() const {
|
||||
#ifdef PLATFORM_SWITCH
|
||||
return touching_;
|
||||
|
|
@ -452,11 +603,25 @@ bool Input::isAnyMouseDown() const {
|
|||
// 视口适配器
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置视口适配器
|
||||
*
|
||||
* 用于将屏幕坐标转换为逻辑坐标。
|
||||
*
|
||||
* @param adapter 视口适配器指针
|
||||
*/
|
||||
void Input::setViewportAdapter(ViewportAdapter* adapter) {
|
||||
viewportAdapter_ = adapter;
|
||||
}
|
||||
|
||||
Vec2 Input::getMousePositionLogic() const {
|
||||
/**
|
||||
* @brief 获取逻辑坐标系的鼠标位置
|
||||
*
|
||||
* 通过视口适配器将屏幕坐标转换为逻辑坐标。
|
||||
*
|
||||
* @return 逻辑坐标系的鼠标位置
|
||||
*/
|
||||
Vec2 Input::getMousePosLogic() const {
|
||||
Vec2 screenPos = getMousePosition();
|
||||
if (viewportAdapter_) {
|
||||
return viewportAdapter_->screenToLogic(screenPos);
|
||||
|
|
@ -464,7 +629,14 @@ Vec2 Input::getMousePositionLogic() const {
|
|||
return screenPos;
|
||||
}
|
||||
|
||||
Vec2 Input::getTouchPositionLogic() const {
|
||||
/**
|
||||
* @brief 获取逻辑坐标系的触摸位置
|
||||
*
|
||||
* 通过视口适配器将屏幕坐标转换为逻辑坐标。
|
||||
*
|
||||
* @return 逻辑坐标系的触摸位置
|
||||
*/
|
||||
Vec2 Input::getTouchPosLogic() const {
|
||||
Vec2 screenPos = getTouchPosition();
|
||||
if (viewportAdapter_) {
|
||||
return viewportAdapter_->screenToLogic(screenPos);
|
||||
|
|
@ -472,6 +644,13 @@ Vec2 Input::getTouchPositionLogic() const {
|
|||
return screenPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取逻辑坐标系的鼠标移动增量
|
||||
*
|
||||
* 根据视口缩放比例调整鼠标移动量。
|
||||
*
|
||||
* @return 逻辑坐标系的鼠标移动增量
|
||||
*/
|
||||
Vec2 Input::getMouseDeltaLogic() const {
|
||||
Vec2 delta = getMouseDelta();
|
||||
if (viewportAdapter_) {
|
||||
|
|
|
|||
|
|
@ -9,19 +9,38 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 初始化窗口的所有成员变量为默认值,包括SDL窗口指针、OpenGL上下文、
|
||||
* 光标数组、窗口尺寸、VSync状态等。
|
||||
*/
|
||||
Window::Window()
|
||||
: sdlWindow_(nullptr), glContext_(nullptr), currentCursor_(nullptr),
|
||||
width_(1280), height_(720), vsync_(true), shouldClose_(false),
|
||||
fullscreen_(true), focused_(true), contentScaleX_(1.0f), contentScaleY_(1.0f),
|
||||
enableDpiScale_(true), userData_(nullptr), eventQueue_(nullptr) {
|
||||
// 初始化光标数组
|
||||
fullscreen_(true), focused_(true), contentScaleX_(1.0f),
|
||||
contentScaleY_(1.0f), enableDpiScale_(true), userData_(nullptr),
|
||||
eventQueue_(nullptr) {
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
sdlCursors_[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 自动调用destroy()方法释放所有资源。
|
||||
*/
|
||||
Window::~Window() { destroy(); }
|
||||
|
||||
/**
|
||||
* @brief 创建窗口
|
||||
*
|
||||
* 根据配置参数创建SDL窗口和OpenGL ES上下文,初始化输入管理器和光标。
|
||||
*
|
||||
* @param config 窗口配置参数,包含尺寸、标题、全屏模式等信息
|
||||
* @return 创建成功返回true,失败返回false
|
||||
*/
|
||||
bool Window::create(const WindowConfig &config) {
|
||||
if (sdlWindow_ != nullptr) {
|
||||
E2D_LOG_WARN("Window already created");
|
||||
|
|
@ -34,17 +53,14 @@ bool Window::create(const WindowConfig &config) {
|
|||
fullscreen_ = config.fullscreen;
|
||||
enableDpiScale_ = config.enableDpiScale;
|
||||
|
||||
// 初始化 SDL2 + 创建窗口 + GL 上下文
|
||||
if (!initSDL(config)) {
|
||||
E2D_LOG_ERROR("Failed to initialize SDL2");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建输入管理器
|
||||
input_ = makeUnique<Input>();
|
||||
input_->init();
|
||||
|
||||
// 初始化光标
|
||||
if (config.enableCursors) {
|
||||
initCursors();
|
||||
}
|
||||
|
|
@ -53,19 +69,26 @@ bool Window::create(const WindowConfig &config) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化SDL库和OpenGL上下文
|
||||
*
|
||||
* 执行SDL2全局初始化、设置OpenGL ES 3.2上下文属性、创建窗口、
|
||||
* 创建OpenGL上下文并加载GLES函数指针。
|
||||
*
|
||||
* @param config 窗口配置参数
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool Window::initSDL(const WindowConfig &config) {
|
||||
// SDL2 全局初始化(视频 + 游戏控制器 + 音频)
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_AUDIO) != 0) {
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_AUDIO) !=
|
||||
0) {
|
||||
E2D_LOG_ERROR("SDL_Init failed: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置 OpenGL ES 3.2 上下文属性
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
|
||||
// 颜色/深度/模板缓冲配置
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
||||
|
|
@ -73,16 +96,11 @@ bool Window::initSDL(const WindowConfig &config) {
|
|||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
|
||||
// 双缓冲
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
||||
// 创建 SDL2 窗口
|
||||
Uint32 windowFlags = SDL_WINDOW_OPENGL;
|
||||
|
||||
// 根据配置设置窗口模式
|
||||
if (config.fullscreen) {
|
||||
// Switch 平台使用 SDL_WINDOW_FULLSCREEN(固定分辨率)
|
||||
// PC 平台使用 SDL_WINDOW_FULLSCREEN_DESKTOP(桌面全屏)
|
||||
if (config.fullscreenDesktop) {
|
||||
windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
} else {
|
||||
|
|
@ -92,8 +110,6 @@ bool Window::initSDL(const WindowConfig &config) {
|
|||
if (config.resizable) {
|
||||
windowFlags |= SDL_WINDOW_RESIZABLE;
|
||||
}
|
||||
// 注意:SDL_WINDOWPOS_CENTERED 是位置参数,不是窗口标志
|
||||
// 窗口居中在 SDL_CreateWindow 的位置参数中处理
|
||||
}
|
||||
|
||||
sdlWindow_ = SDL_CreateWindow(
|
||||
|
|
@ -108,7 +124,6 @@ bool Window::initSDL(const WindowConfig &config) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 创建 OpenGL ES 上下文
|
||||
glContext_ = SDL_GL_CreateContext(sdlWindow_);
|
||||
if (!glContext_) {
|
||||
E2D_LOG_ERROR("SDL_GL_CreateContext failed: {}", SDL_GetError());
|
||||
|
|
@ -128,8 +143,8 @@ bool Window::initSDL(const WindowConfig &config) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 加载 OpenGL ES 函数指针
|
||||
if (gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress)) == 0) {
|
||||
if (gladLoadGLES2Loader(
|
||||
reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress)) == 0) {
|
||||
E2D_LOG_ERROR("gladLoadGLES2Loader failed");
|
||||
SDL_GL_DeleteContext(glContext_);
|
||||
glContext_ = nullptr;
|
||||
|
|
@ -139,10 +154,8 @@ bool Window::initSDL(const WindowConfig &config) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 设置 VSync
|
||||
SDL_GL_SetSwapInterval(vsync_ ? 1 : 0);
|
||||
|
||||
// 更新 DPI 缩放
|
||||
if (config.enableDpiScale) {
|
||||
updateContentScale();
|
||||
}
|
||||
|
|
@ -156,6 +169,11 @@ bool Window::initSDL(const WindowConfig &config) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 反初始化SDL资源
|
||||
*
|
||||
* 释放光标资源、删除OpenGL上下文、销毁SDL窗口并退出SDL库。
|
||||
*/
|
||||
void Window::deinitSDL() {
|
||||
deinitCursors();
|
||||
|
||||
|
|
@ -172,6 +190,11 @@ void Window::deinitSDL() {
|
|||
SDL_Quit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 销毁窗口
|
||||
*
|
||||
* 释放输入管理器并调用deinitSDL()清理所有SDL相关资源。
|
||||
*/
|
||||
void Window::destroy() {
|
||||
if (sdlWindow_ != nullptr) {
|
||||
input_.reset();
|
||||
|
|
@ -180,8 +203,13 @@ void Window::destroy() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 轮询并处理窗口事件
|
||||
*
|
||||
* 处理SDL事件队列中的所有事件,包括窗口关闭、大小改变、焦点变化等,
|
||||
* 并更新输入管理器状态。
|
||||
*/
|
||||
void Window::pollEvents() {
|
||||
// SDL2 事件循环
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
|
|
@ -220,28 +248,53 @@ void Window::pollEvents() {
|
|||
}
|
||||
}
|
||||
|
||||
// 输入更新
|
||||
if (input_) {
|
||||
input_->update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 交换前后缓冲区
|
||||
*
|
||||
* 将后台缓冲区内容呈现到屏幕上,实现双缓冲渲染。
|
||||
*/
|
||||
void Window::swapBuffers() {
|
||||
if (sdlWindow_) {
|
||||
SDL_GL_SwapWindow(sdlWindow_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查窗口是否应该关闭
|
||||
*
|
||||
* @return 如果窗口应该关闭返回true,否则返回false
|
||||
*/
|
||||
bool Window::shouldClose() const { return shouldClose_; }
|
||||
|
||||
/**
|
||||
* @brief 设置窗口关闭标志
|
||||
*
|
||||
* @param close 是否应该关闭窗口
|
||||
*/
|
||||
void Window::setShouldClose(bool close) { shouldClose_ = close; }
|
||||
|
||||
/**
|
||||
* @brief 设置窗口标题
|
||||
*
|
||||
* @param title 新的窗口标题字符串
|
||||
*/
|
||||
void Window::setTitle(const std::string &title) {
|
||||
if (sdlWindow_) {
|
||||
SDL_SetWindowTitle(sdlWindow_, title.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置窗口大小
|
||||
*
|
||||
* @param width 新的窗口宽度(像素)
|
||||
* @param height 新的窗口高度(像素)
|
||||
*/
|
||||
void Window::setSize(int width, int height) {
|
||||
if (sdlWindow_) {
|
||||
SDL_SetWindowSize(sdlWindow_, width, height);
|
||||
|
|
@ -250,32 +303,57 @@ void Window::setSize(int width, int height) {
|
|||
}
|
||||
}
|
||||
|
||||
void Window::setPosition(int x, int y) {
|
||||
/**
|
||||
* @brief 设置窗口位置
|
||||
*
|
||||
* @param x 窗口左上角的X坐标(屏幕坐标)
|
||||
* @param y 窗口左上角的Y坐标(屏幕坐标)
|
||||
*/
|
||||
void Window::setPos(int x, int y) {
|
||||
if (sdlWindow_) {
|
||||
SDL_SetWindowPosition(sdlWindow_, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置窗口全屏模式
|
||||
*
|
||||
* @param fullscreen true为全屏模式,false为窗口模式
|
||||
*/
|
||||
void Window::setFullscreen(bool fullscreen) {
|
||||
if (sdlWindow_) {
|
||||
// 默认使用桌面全屏模式(PC 平台)
|
||||
Uint32 flags = fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0;
|
||||
SDL_SetWindowFullscreen(sdlWindow_, flags);
|
||||
fullscreen_ = fullscreen;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置垂直同步
|
||||
*
|
||||
* @param enabled true启用VSync,false禁用VSync
|
||||
*/
|
||||
void Window::setVSync(bool enabled) {
|
||||
vsync_ = enabled;
|
||||
SDL_GL_SetSwapInterval(enabled ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置窗口是否可调整大小
|
||||
*
|
||||
* @param resizable true允许用户调整窗口大小,false禁止调整
|
||||
*/
|
||||
void Window::setResizable(bool resizable) {
|
||||
if (sdlWindow_) {
|
||||
SDL_SetWindowResizable(sdlWindow_, resizable ? SDL_TRUE : SDL_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取窗口位置
|
||||
*
|
||||
* @return 包含窗口左上角X和Y坐标的二维向量
|
||||
*/
|
||||
Vec2 Window::getPosition() const {
|
||||
if (sdlWindow_) {
|
||||
int x, y;
|
||||
|
|
@ -285,18 +363,42 @@ Vec2 Window::getPosition() const {
|
|||
return Vec2::Zero();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取X轴内容缩放比例
|
||||
*
|
||||
* 根据DPI设置返回X轴的内容缩放比例,用于高DPI显示适配。
|
||||
*
|
||||
* @return X轴缩放比例,如果DPI缩放被禁用则返回1.0
|
||||
*/
|
||||
float Window::getContentScaleX() const {
|
||||
return enableDpiScale_ ? contentScaleX_ : 1.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取Y轴内容缩放比例
|
||||
*
|
||||
* 根据DPI设置返回Y轴的内容缩放比例,用于高DPI显示适配。
|
||||
*
|
||||
* @return Y轴缩放比例,如果DPI缩放被禁用则返回1.0
|
||||
*/
|
||||
float Window::getContentScaleY() const {
|
||||
return enableDpiScale_ ? contentScaleY_ : 1.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取内容缩放比例
|
||||
*
|
||||
* @return 包含X和Y轴缩放比例的二维向量
|
||||
*/
|
||||
Vec2 Window::getContentScale() const {
|
||||
return Vec2(getContentScaleX(), getContentScaleY());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查窗口是否最小化
|
||||
*
|
||||
* @return 如果窗口处于最小化状态返回true,否则返回false
|
||||
*/
|
||||
bool Window::isMinimized() const {
|
||||
if (sdlWindow_) {
|
||||
Uint32 flags = SDL_GetWindowFlags(sdlWindow_);
|
||||
|
|
@ -305,6 +407,11 @@ bool Window::isMinimized() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查窗口是否最大化
|
||||
*
|
||||
* @return 如果窗口处于最大化状态返回true,否则返回false
|
||||
*/
|
||||
bool Window::isMaximized() const {
|
||||
if (sdlWindow_) {
|
||||
Uint32 flags = SDL_GetWindowFlags(sdlWindow_);
|
||||
|
|
@ -313,6 +420,12 @@ bool Window::isMaximized() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化系统光标
|
||||
*
|
||||
* 创建9种常用的系统光标,包括箭头、文本选择、十字、手形、
|
||||
* 水平调整、垂直调整、移动、对角线调整等。
|
||||
*/
|
||||
void Window::initCursors() {
|
||||
sdlCursors_[0] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
|
||||
sdlCursors_[1] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
|
||||
|
|
@ -325,6 +438,11 @@ void Window::initCursors() {
|
|||
sdlCursors_[8] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放光标资源
|
||||
*
|
||||
* 释放所有已创建的系统光标并重置当前光标指针。
|
||||
*/
|
||||
void Window::deinitCursors() {
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
if (sdlCursors_[i]) {
|
||||
|
|
@ -335,6 +453,13 @@ void Window::deinitCursors() {
|
|||
currentCursor_ = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置鼠标光标样式
|
||||
*
|
||||
* 将鼠标光标更改为指定的系统光标样式。
|
||||
*
|
||||
* @param shape 光标形状枚举值
|
||||
*/
|
||||
void Window::setCursor(CursorShape shape) {
|
||||
int index = static_cast<int>(shape);
|
||||
if (index >= 0 && index < 9 && sdlCursors_[index]) {
|
||||
|
|
@ -343,23 +468,37 @@ void Window::setCursor(CursorShape shape) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 重置鼠标光标为默认样式
|
||||
*
|
||||
* 将鼠标光标恢复为系统默认光标。
|
||||
*/
|
||||
void Window::resetCursor() {
|
||||
SDL_SetCursor(SDL_GetDefaultCursor());
|
||||
currentCursor_ = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置鼠标光标可见性
|
||||
*
|
||||
* @param visible true显示鼠标光标,false隐藏鼠标光标
|
||||
*/
|
||||
void Window::setMouseVisible(bool visible) {
|
||||
SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新内容缩放比例
|
||||
*
|
||||
* 根据当前窗口所在显示器的DPI值更新内容缩放比例,
|
||||
* 以标准96 DPI为基准计算缩放因子。
|
||||
*/
|
||||
void Window::updateContentScale() {
|
||||
if (sdlWindow_) {
|
||||
// 使用 DPI 计算内容缩放比例
|
||||
int displayIndex = SDL_GetWindowDisplayIndex(sdlWindow_);
|
||||
if (displayIndex >= 0) {
|
||||
float ddpi, hdpi, vdpi;
|
||||
if (SDL_GetDisplayDPI(displayIndex, &ddpi, &hdpi, &vdpi) == 0) {
|
||||
// 假设标准 DPI 为 96
|
||||
contentScaleX_ = hdpi / 96.0f;
|
||||
contentScaleY_ = vdpi / 96.0f;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,18 +7,32 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 创建一个空的节点对象
|
||||
*/
|
||||
Node::Node() = default;
|
||||
|
||||
Node::~Node() {
|
||||
removeAllChildren();
|
||||
}
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*
|
||||
* 清除所有子节点
|
||||
*/
|
||||
Node::~Node() { clearChildren(); }
|
||||
|
||||
/**
|
||||
* @brief 添加子节点
|
||||
* @param child 要添加的子节点智能指针
|
||||
*
|
||||
* 将子节点添加到当前节点的子节点列表中,自动从原父节点分离
|
||||
*/
|
||||
void Node::addChild(Ptr<Node> child) {
|
||||
if (!child || child.get() == this) {
|
||||
return;
|
||||
}
|
||||
|
||||
child->removeFromParent();
|
||||
child->detach();
|
||||
child->parent_ = weak_from_this();
|
||||
children_.push_back(child);
|
||||
childrenOrderDirty_ = true;
|
||||
|
|
@ -39,6 +53,12 @@ void Node::addChild(Ptr<Node> child) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 批量添加子节点
|
||||
* @param children 要添加的子节点数组(右值引用)
|
||||
*
|
||||
* 高效地批量添加多个子节点,预分配内存以减少扩容次数
|
||||
*/
|
||||
void Node::addChildren(std::vector<Ptr<Node>> &&children) {
|
||||
// 预留空间,避免多次扩容
|
||||
size_t newSize = children_.size() + children.size();
|
||||
|
|
@ -51,7 +71,7 @@ void Node::addChildren(std::vector<Ptr<Node>> &&children) {
|
|||
continue;
|
||||
}
|
||||
|
||||
child->removeFromParent();
|
||||
child->detach();
|
||||
child->parent_ = weak_from_this();
|
||||
children_.push_back(child);
|
||||
|
||||
|
|
@ -76,6 +96,12 @@ void Node::addChildren(std::vector<Ptr<Node>> &&children) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移除子节点
|
||||
* @param child 要移除的子节点智能指针
|
||||
*
|
||||
* 从子节点列表中移除指定节点,并触发相应的退出回调
|
||||
*/
|
||||
void Node::removeChild(Ptr<Node> child) {
|
||||
if (!child)
|
||||
return;
|
||||
|
|
@ -98,14 +124,25 @@ void Node::removeChild(Ptr<Node> child) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 通过名称移除子节点
|
||||
* @param name 子节点的名称
|
||||
*
|
||||
* 查找并移除具有指定名称的子节点
|
||||
*/
|
||||
void Node::removeChildByName(const std::string &name) {
|
||||
auto child = getChildByName(name);
|
||||
auto child = findChild(name);
|
||||
if (child) {
|
||||
removeChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
void Node::removeFromParent() {
|
||||
/**
|
||||
* @brief 从父节点分离
|
||||
*
|
||||
* 将当前节点从其父节点的子节点列表中移除
|
||||
*/
|
||||
void Node::detach() {
|
||||
auto p = parent_.lock();
|
||||
if (p) {
|
||||
// 安全获取 shared_ptr,避免在对象未由 shared_ptr 管理时崩溃
|
||||
|
|
@ -121,7 +158,12 @@ void Node::removeFromParent() {
|
|||
}
|
||||
}
|
||||
|
||||
void Node::removeAllChildren() {
|
||||
/**
|
||||
* @brief 清除所有子节点
|
||||
*
|
||||
* 移除所有子节点并触发相应的退出回调
|
||||
*/
|
||||
void Node::clearChildren() {
|
||||
for (auto &child : children_) {
|
||||
if (running_) {
|
||||
child->onDetachFromScene();
|
||||
|
|
@ -134,7 +176,14 @@ void Node::removeAllChildren() {
|
|||
tagIndex_.clear();
|
||||
}
|
||||
|
||||
Ptr<Node> Node::getChildByName(const std::string &name) const {
|
||||
/**
|
||||
* @brief 通过名称查找子节点
|
||||
* @param name 子节点的名称
|
||||
* @return 找到的子节点智能指针,未找到返回nullptr
|
||||
*
|
||||
* 使用哈希索引进行O(1)时间复杂度查找
|
||||
*/
|
||||
Ptr<Node> Node::findChild(const std::string &name) const {
|
||||
// 使用哈希索引,O(1) 查找
|
||||
auto it = nameIndex_.find(name);
|
||||
if (it != nameIndex_.end()) {
|
||||
|
|
@ -143,7 +192,14 @@ Ptr<Node> Node::getChildByName(const std::string &name) const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Ptr<Node> Node::getChildByTag(int tag) const {
|
||||
/**
|
||||
* @brief 通过标签查找子节点
|
||||
* @param tag 子节点的标签值
|
||||
* @return 找到的子节点智能指针,未找到返回nullptr
|
||||
*
|
||||
* 使用哈希索引进行O(1)时间复杂度查找
|
||||
*/
|
||||
Ptr<Node> Node::findChildByTag(int tag) const {
|
||||
// 使用哈希索引,O(1) 查找
|
||||
auto it = tagIndex_.find(tag);
|
||||
if (it != tagIndex_.end()) {
|
||||
|
|
@ -152,53 +208,123 @@ Ptr<Node> Node::getChildByTag(int tag) const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void Node::setPosition(const Vec2 &pos) {
|
||||
/**
|
||||
* @brief 设置节点位置
|
||||
* @param pos 新的位置坐标
|
||||
*/
|
||||
void Node::setPos(const Vec2 &pos) {
|
||||
position_ = pos;
|
||||
markTransformDirty();
|
||||
}
|
||||
|
||||
void Node::setPosition(float x, float y) { setPosition(Vec2(x, y)); }
|
||||
/**
|
||||
* @brief 设置节点位置
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
*/
|
||||
void Node::setPos(float x, float y) { setPos(Vec2(x, y)); }
|
||||
|
||||
/**
|
||||
* @brief 设置节点旋转角度
|
||||
* @param degrees 旋转角度(度数)
|
||||
*/
|
||||
void Node::setRotation(float degrees) {
|
||||
rotation_ = degrees;
|
||||
markTransformDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置节点缩放
|
||||
* @param scale 缩放向量
|
||||
*/
|
||||
void Node::setScale(const Vec2 &scale) {
|
||||
scale_ = scale;
|
||||
markTransformDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置节点统一缩放
|
||||
* @param scale 统一缩放值
|
||||
*/
|
||||
void Node::setScale(float scale) { setScale(Vec2(scale, scale)); }
|
||||
|
||||
/**
|
||||
* @brief 设置节点缩放
|
||||
* @param x X轴缩放值
|
||||
* @param y Y轴缩放值
|
||||
*/
|
||||
void Node::setScale(float x, float y) { setScale(Vec2(x, y)); }
|
||||
|
||||
/**
|
||||
* @brief 设置节点锚点
|
||||
* @param anchor 锚点位置(0-1范围)
|
||||
*/
|
||||
void Node::setAnchor(const Vec2 &anchor) {
|
||||
anchor_ = anchor;
|
||||
markTransformDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置节点锚点
|
||||
* @param x 锚点X坐标(0-1范围)
|
||||
* @param y 锚点Y坐标(0-1范围)
|
||||
*/
|
||||
void Node::setAnchor(float x, float y) { setAnchor(Vec2(x, y)); }
|
||||
|
||||
/**
|
||||
* @brief 设置节点斜切
|
||||
* @param skew 斜切角度向量
|
||||
*/
|
||||
void Node::setSkew(const Vec2 &skew) {
|
||||
skew_ = skew;
|
||||
markTransformDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置节点斜切
|
||||
* @param x X轴斜切角度
|
||||
* @param y Y轴斜切角度
|
||||
*/
|
||||
void Node::setSkew(float x, float y) { setSkew(Vec2(x, y)); }
|
||||
|
||||
/**
|
||||
* @brief 设置节点透明度
|
||||
* @param opacity 透明度值(0.0-1.0范围)
|
||||
*/
|
||||
void Node::setOpacity(float opacity) {
|
||||
opacity_ = std::clamp(opacity, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置节点可见性
|
||||
* @param visible 是否可见
|
||||
*/
|
||||
void Node::setVisible(bool visible) { visible_ = visible; }
|
||||
|
||||
/**
|
||||
* @brief 设置节点颜色
|
||||
* @param color RGB颜色值
|
||||
*/
|
||||
void Node::setColor(const Color3B &color) { color_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置水平翻转
|
||||
* @param flipX 是否水平翻转
|
||||
*/
|
||||
void Node::setFlipX(bool flipX) { flipX_ = flipX; }
|
||||
|
||||
/**
|
||||
* @brief 设置垂直翻转
|
||||
* @param flipY 是否垂直翻转
|
||||
*/
|
||||
void Node::setFlipY(bool flipY) { flipY_ = flipY; }
|
||||
|
||||
/**
|
||||
* @brief 设置Z序
|
||||
* @param zOrder 渲染层级顺序
|
||||
*
|
||||
* 较大的Z序值会在上层渲染
|
||||
*/
|
||||
void Node::setZOrder(int zOrder) {
|
||||
if (zOrder_ != zOrder) {
|
||||
zOrder_ = zOrder;
|
||||
|
|
@ -206,18 +332,34 @@ void Node::setZOrder(int zOrder) {
|
|||
}
|
||||
}
|
||||
|
||||
Vec2 Node::convertToWorldSpace(const Vec2 &localPos) const {
|
||||
/**
|
||||
* @brief 将本地坐标转换为世界坐标
|
||||
* @param localPos 本地坐标位置
|
||||
* @return 世界坐标位置
|
||||
*/
|
||||
Vec2 Node::toWorld(const Vec2 &localPos) const {
|
||||
glm::vec4 worldPos =
|
||||
getWorldTransform() * glm::vec4(localPos.x, localPos.y, 0.0f, 1.0f);
|
||||
return Vec2(worldPos.x, worldPos.y);
|
||||
}
|
||||
|
||||
Vec2 Node::convertToNodeSpace(const Vec2 &worldPos) const {
|
||||
/**
|
||||
* @brief 将世界坐标转换为本地坐标
|
||||
* @param worldPos 世界坐标位置
|
||||
* @return 本地坐标位置
|
||||
*/
|
||||
Vec2 Node::toLocal(const Vec2 &worldPos) const {
|
||||
glm::mat4 invWorld = glm::inverse(getWorldTransform());
|
||||
glm::vec4 localPos = invWorld * glm::vec4(worldPos.x, worldPos.y, 0.0f, 1.0f);
|
||||
return Vec2(localPos.x, localPos.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取本地变换矩阵
|
||||
* @return 本地变换矩阵
|
||||
*
|
||||
* 计算包含位置、旋转、斜切和缩放的本地变换矩阵
|
||||
*/
|
||||
glm::mat4 Node::getLocalTransform() const {
|
||||
if (transformDirty_) {
|
||||
localTransform_ = glm::mat4(1.0f);
|
||||
|
|
@ -249,6 +391,12 @@ glm::mat4 Node::getLocalTransform() const {
|
|||
return localTransform_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取世界变换矩阵
|
||||
* @return 世界变换矩阵
|
||||
*
|
||||
* 计算从根节点到当前节点的累积变换矩阵
|
||||
*/
|
||||
glm::mat4 Node::getWorldTransform() const {
|
||||
if (worldTransformDirty_) {
|
||||
// 使用线程局部存储的固定数组,避免每帧内存分配
|
||||
|
|
@ -275,6 +423,11 @@ glm::mat4 Node::getWorldTransform() const {
|
|||
return worldTransform_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 标记变换为脏
|
||||
*
|
||||
* 标记本地变换和世界变换需要重新计算,并递归标记所有子节点
|
||||
*/
|
||||
void Node::markTransformDirty() {
|
||||
// 避免重复标记,提高性能
|
||||
if (!transformDirty_ || !worldTransformDirty_) {
|
||||
|
|
@ -288,7 +441,12 @@ void Node::markTransformDirty() {
|
|||
}
|
||||
}
|
||||
|
||||
void Node::batchUpdateTransforms() {
|
||||
/**
|
||||
* @brief 批量更新变换
|
||||
*
|
||||
* 从父节点到子节点依次更新世界变换矩阵
|
||||
*/
|
||||
void Node::batchTransforms() {
|
||||
// 如果本地变换脏了,先计算本地变换
|
||||
if (transformDirty_) {
|
||||
(void)getLocalTransform(); // 这会计算并缓存本地变换
|
||||
|
|
@ -309,10 +467,15 @@ void Node::batchUpdateTransforms() {
|
|||
|
||||
// 递归更新子节点
|
||||
for (auto &child : children_) {
|
||||
child->batchUpdateTransforms();
|
||||
child->batchTransforms();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 节点进入时的回调
|
||||
*
|
||||
* 标记节点为运行状态,并递归调用所有子节点的onEnter
|
||||
*/
|
||||
void Node::onEnter() {
|
||||
running_ = true;
|
||||
for (auto &child : children_) {
|
||||
|
|
@ -320,6 +483,11 @@ void Node::onEnter() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 节点退出时的回调
|
||||
*
|
||||
* 标记节点为非运行状态,并递归调用所有子节点的onExit
|
||||
*/
|
||||
void Node::onExit() {
|
||||
running_ = false;
|
||||
for (auto &child : children_) {
|
||||
|
|
@ -327,6 +495,12 @@ void Node::onExit() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新回调
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*
|
||||
* 先调用节点自身的更新逻辑,再更新所有子节点
|
||||
*/
|
||||
void Node::onUpdate(float dt) {
|
||||
onUpdateNode(dt);
|
||||
|
||||
|
|
@ -336,6 +510,12 @@ void Node::onUpdate(float dt) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染回调
|
||||
* @param renderer 渲染后端引用
|
||||
*
|
||||
* 如果可见则绘制自身,然后递归渲染所有子节点
|
||||
*/
|
||||
void Node::onRender(RenderBackend &renderer) {
|
||||
if (!visible_)
|
||||
return;
|
||||
|
|
@ -347,6 +527,12 @@ void Node::onRender(RenderBackend &renderer) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 附加到场景时的回调
|
||||
* @param scene 所属场景指针
|
||||
*
|
||||
* 设置场景引用并递归通知所有子节点
|
||||
*/
|
||||
void Node::onAttachToScene(Scene *scene) {
|
||||
scene_ = scene;
|
||||
|
||||
|
|
@ -355,6 +541,11 @@ void Node::onAttachToScene(Scene *scene) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从场景分离时的回调
|
||||
*
|
||||
* 清除场景引用并递归通知所有子节点
|
||||
*/
|
||||
void Node::onDetachFromScene() {
|
||||
scene_ = nullptr;
|
||||
for (auto &child : children_) {
|
||||
|
|
@ -362,12 +553,28 @@ void Node::onDetachFromScene() {
|
|||
}
|
||||
}
|
||||
|
||||
Rect Node::getBoundingBox() const {
|
||||
return Rect(position_.x, position_.y, 0, 0);
|
||||
}
|
||||
/**
|
||||
* @brief 获取节点边界矩形
|
||||
* @return 节点的边界矩形
|
||||
*
|
||||
* 默认返回以位置为中心的空矩形,子类应重写此方法
|
||||
*/
|
||||
Rect Node::getBounds() const { return Rect(position_.x, position_.y, 0, 0); }
|
||||
|
||||
/**
|
||||
* @brief 更新节点
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*
|
||||
* 调用onUpdate进行更新
|
||||
*/
|
||||
void Node::update(float dt) { onUpdate(dt); }
|
||||
|
||||
/**
|
||||
* @brief 渲染节点
|
||||
* @param renderer 渲染后端引用
|
||||
*
|
||||
* 如果需要则对子节点排序,然后调用onRender进行渲染
|
||||
*/
|
||||
void Node::render(RenderBackend &renderer) {
|
||||
if (childrenOrderDirty_) {
|
||||
sortChildren();
|
||||
|
|
@ -375,6 +582,11 @@ void Node::render(RenderBackend &renderer) {
|
|||
onRender(renderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 对子节点按Z序排序
|
||||
*
|
||||
* 小数组使用插入排序,大数组使用标准排序以优化性能
|
||||
*/
|
||||
void Node::sortChildren() {
|
||||
// 使用插入排序优化小范围更新场景
|
||||
// 插入排序在大部分已有序的情况下性能接近O(n)
|
||||
|
|
@ -409,6 +621,13 @@ void Node::sortChildren() {
|
|||
childrenOrderDirty_ = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 收集渲染命令
|
||||
* @param commands 渲染命令输出向量
|
||||
* @param parentZOrder 父节点的Z序
|
||||
*
|
||||
* 递归收集当前节点和所有子节点的渲染命令
|
||||
*/
|
||||
void Node::collectRenderCommands(std::vector<RenderCommand> &commands,
|
||||
int parentZOrder) {
|
||||
if (!visible_)
|
||||
|
|
|
|||
|
|
@ -5,10 +5,26 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 构造函数,初始化场景对象
|
||||
*
|
||||
* 创建默认相机实例
|
||||
*/
|
||||
Scene::Scene() { defaultCamera_ = makePtr<Camera>(); }
|
||||
|
||||
/**
|
||||
* @brief 设置场景相机
|
||||
* @param camera 要设置的相机智能指针
|
||||
*/
|
||||
void Scene::setCamera(Ptr<Camera> camera) { camera_ = camera; }
|
||||
|
||||
/**
|
||||
* @brief 设置视口大小
|
||||
* @param width 视口宽度
|
||||
* @param height 视口高度
|
||||
*
|
||||
* 同时更新活动相机的视口参数
|
||||
*/
|
||||
void Scene::setViewportSize(float width, float height) {
|
||||
viewportSize_ = Size(width, height);
|
||||
if (defaultCamera_) {
|
||||
|
|
@ -18,10 +34,20 @@ void Scene::setViewportSize(float width, float height) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置视口大小
|
||||
* @param size 视口尺寸结构体
|
||||
*/
|
||||
void Scene::setViewportSize(const Size &size) {
|
||||
setViewportSize(size.width, size.height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染场景
|
||||
* @param renderer 渲染后端引用
|
||||
*
|
||||
* 如果场景不可见则直接返回,否则开始帧渲染、渲染内容并结束帧
|
||||
*/
|
||||
void Scene::renderScene(RenderBackend &renderer) {
|
||||
if (!isVisible())
|
||||
return;
|
||||
|
|
@ -32,12 +58,18 @@ void Scene::renderScene(RenderBackend &renderer) {
|
|||
renderer.endFrame();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染场景内容
|
||||
* @param renderer 渲染后端引用
|
||||
*
|
||||
* 批量更新节点变换,设置活动相机的视图投影矩阵,开始精灵批处理并渲染
|
||||
*/
|
||||
void Scene::renderContent(RenderBackend &renderer) {
|
||||
if (!isVisible())
|
||||
return;
|
||||
|
||||
// 在渲染前批量更新所有节点的世界变换
|
||||
batchUpdateTransforms();
|
||||
batchTransforms();
|
||||
|
||||
Camera *activeCam = getActiveCamera();
|
||||
if (activeCam) {
|
||||
|
|
@ -49,20 +81,39 @@ void Scene::renderContent(RenderBackend &renderer) {
|
|||
renderer.endSpriteBatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新场景
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*
|
||||
* 如果场景未暂停则调用update方法
|
||||
*/
|
||||
void Scene::updateScene(float dt) {
|
||||
if (!paused_) {
|
||||
update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::onEnter() {
|
||||
Node::onEnter();
|
||||
}
|
||||
/**
|
||||
* @brief 场景进入时的回调函数
|
||||
*
|
||||
* 调用父类Node的onEnter方法
|
||||
*/
|
||||
void Scene::onEnter() { Node::onEnter(); }
|
||||
|
||||
void Scene::onExit() {
|
||||
Node::onExit();
|
||||
}
|
||||
/**
|
||||
* @brief 场景退出时的回调函数
|
||||
*
|
||||
* 调用父类Node的onExit方法
|
||||
*/
|
||||
void Scene::onExit() { Node::onExit(); }
|
||||
|
||||
/**
|
||||
* @brief 收集渲染命令
|
||||
* @param commands 渲染命令输出向量
|
||||
* @param parentZOrder 父节点的Z序
|
||||
*
|
||||
* 如果场景不可见则直接返回,否则从场景的子节点开始收集渲染命令
|
||||
*/
|
||||
void Scene::collectRenderCommands(std::vector<RenderCommand> &commands,
|
||||
int parentZOrder) {
|
||||
if (!isVisible())
|
||||
|
|
@ -72,6 +123,10 @@ void Scene::collectRenderCommands(std::vector<RenderCommand> &commands,
|
|||
Node::collectRenderCommands(commands, parentZOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建场景对象
|
||||
* @return 新创建的场景智能指针
|
||||
*/
|
||||
Ptr<Scene> Scene::create() { return makePtr<Scene>(); }
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ namespace {
|
|||
|
||||
/**
|
||||
* @brief 命中测试 - 从节点树中找到最上层的可交互节点
|
||||
* @param node 要测试的节点
|
||||
* @param worldPos 世界坐标位置
|
||||
* @return 命中的节点指针,未命中返回nullptr
|
||||
*/
|
||||
Node *hitTestTopmost(const Ptr<Node> &node, const Vec2 &worldPos) {
|
||||
if (!node || !node->isVisible()) {
|
||||
|
|
@ -34,7 +37,7 @@ Node *hitTestTopmost(const Ptr<Node> &node, const Vec2 &worldPos) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Rect bounds = node->getBoundingBox();
|
||||
Rect bounds = node->getBounds();
|
||||
if (!bounds.empty() && bounds.containsPoint(worldPos)) {
|
||||
return node.get();
|
||||
}
|
||||
|
|
@ -44,6 +47,8 @@ Node *hitTestTopmost(const Ptr<Node> &node, const Vec2 &worldPos) {
|
|||
|
||||
/**
|
||||
* @brief 向节点分发事件
|
||||
* @param node 目标节点
|
||||
* @param event 要分发的事件
|
||||
*/
|
||||
void dispatchToNode(Node *node, Event &event) {
|
||||
if (!node) {
|
||||
|
|
@ -54,11 +59,21 @@ void dispatchToNode(Node *node, Event &event) {
|
|||
|
||||
} // namespace
|
||||
|
||||
SceneManager &SceneManager::getInstance() {
|
||||
/**
|
||||
* @brief 获取场景管理器单例
|
||||
* @return 场景管理器的全局唯一实例引用
|
||||
*/
|
||||
SceneManager &SceneManager::get() {
|
||||
static SceneManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 运行指定场景
|
||||
* @param scene 要运行的场景智能指针
|
||||
*
|
||||
* 此方法应在应用启动时调用一次,设置初始场景
|
||||
*/
|
||||
void SceneManager::runWithScene(Ptr<Scene> scene) {
|
||||
if (!scene) {
|
||||
return;
|
||||
|
|
@ -74,6 +89,12 @@ void SceneManager::runWithScene(Ptr<Scene> scene) {
|
|||
sceneStack_.push(scene);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 替换当前场景
|
||||
* @param scene 新场景智能指针
|
||||
*
|
||||
* 移除当前场景并替换为新场景,场景栈大小保持不变
|
||||
*/
|
||||
void SceneManager::replaceScene(Ptr<Scene> scene) {
|
||||
if (!scene || isTransitioning_) {
|
||||
return;
|
||||
|
|
@ -94,6 +115,12 @@ void SceneManager::replaceScene(Ptr<Scene> scene) {
|
|||
sceneStack_.push(scene);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 进入场景
|
||||
* @param scene 要进入的场景智能指针
|
||||
*
|
||||
* 如果场景栈为空则运行场景,否则替换当前场景
|
||||
*/
|
||||
void SceneManager::enterScene(Ptr<Scene> scene) {
|
||||
if (!scene || isTransitioning_) {
|
||||
return;
|
||||
|
|
@ -106,6 +133,12 @@ void SceneManager::enterScene(Ptr<Scene> scene) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 压入场景到栈顶
|
||||
* @param scene 要压入的场景智能指针
|
||||
*
|
||||
* 将新场景压入栈顶,暂停当前场景
|
||||
*/
|
||||
void SceneManager::pushScene(Ptr<Scene> scene) {
|
||||
if (!scene || isTransitioning_) {
|
||||
return;
|
||||
|
|
@ -120,6 +153,11 @@ void SceneManager::pushScene(Ptr<Scene> scene) {
|
|||
sceneStack_.push(scene);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 弹出当前场景
|
||||
*
|
||||
* 移除栈顶场景并恢复上一个场景
|
||||
*/
|
||||
void SceneManager::popScene() {
|
||||
if (sceneStack_.size() <= 1 || isTransitioning_) {
|
||||
return;
|
||||
|
|
@ -135,6 +173,11 @@ void SceneManager::popScene() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 弹出到根场景
|
||||
*
|
||||
* 移除所有场景直到只剩根场景
|
||||
*/
|
||||
void SceneManager::popToRootScene() {
|
||||
if (sceneStack_.size() <= 1 || isTransitioning_) {
|
||||
return;
|
||||
|
|
@ -150,6 +193,12 @@ void SceneManager::popToRootScene() {
|
|||
sceneStack_.top()->resume();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 弹出到指定名称的场景
|
||||
* @param name 目标场景的名称
|
||||
*
|
||||
* 移除栈顶场景直到找到指定名称的场景
|
||||
*/
|
||||
void SceneManager::popToScene(const std::string &name) {
|
||||
if (isTransitioning_) {
|
||||
return;
|
||||
|
|
@ -174,6 +223,10 @@ void SceneManager::popToScene(const std::string &name) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前场景
|
||||
* @return 当前栈顶场景的智能指针,栈为空时返回nullptr
|
||||
*/
|
||||
Ptr<Scene> SceneManager::getCurrentScene() const {
|
||||
if (sceneStack_.empty()) {
|
||||
return nullptr;
|
||||
|
|
@ -181,6 +234,10 @@ Ptr<Scene> SceneManager::getCurrentScene() const {
|
|||
return sceneStack_.top();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取前一个场景
|
||||
* @return 栈顶下一个场景的智能指针,不存在时返回nullptr
|
||||
*/
|
||||
Ptr<Scene> SceneManager::getPreviousScene() const {
|
||||
if (sceneStack_.size() < 2) {
|
||||
return nullptr;
|
||||
|
|
@ -191,6 +248,10 @@ Ptr<Scene> SceneManager::getPreviousScene() const {
|
|||
return tempStack.top();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取根场景
|
||||
* @return 栈底场景的智能指针,栈为空时返回nullptr
|
||||
*/
|
||||
Ptr<Scene> SceneManager::getRootScene() const {
|
||||
if (sceneStack_.empty()) {
|
||||
return nullptr;
|
||||
|
|
@ -205,6 +266,11 @@ Ptr<Scene> SceneManager::getRootScene() const {
|
|||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 通过名称获取场景
|
||||
* @param name 场景名称
|
||||
* @return 找到的场景智能指针,未找到返回nullptr
|
||||
*/
|
||||
Ptr<Scene> SceneManager::getSceneByName(const std::string &name) const {
|
||||
auto it = namedScenes_.find(name);
|
||||
if (it != namedScenes_.end()) {
|
||||
|
|
@ -223,10 +289,21 @@ Ptr<Scene> SceneManager::getSceneByName(const std::string &name) const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查是否存在指定名称的场景
|
||||
* @param name 场景名称
|
||||
* @return 存在返回true,否则返回false
|
||||
*/
|
||||
bool SceneManager::hasScene(const std::string &name) const {
|
||||
return getSceneByName(name) != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新场景管理器
|
||||
* @param dt 帧间隔时间(秒)
|
||||
*
|
||||
* 更新当前场景并分发指针事件
|
||||
*/
|
||||
void SceneManager::update(float dt) {
|
||||
if (isTransitioning_) {
|
||||
hoverTarget_ = nullptr;
|
||||
|
|
@ -243,6 +320,12 @@ void SceneManager::update(float dt) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染当前场景
|
||||
* @param renderer 渲染后端引用
|
||||
*
|
||||
* 使用当前场景的背景色清除帧缓冲并渲染场景内容
|
||||
*/
|
||||
void SceneManager::render(RenderBackend &renderer) {
|
||||
Color clearColor = Colors::Black;
|
||||
if (!sceneStack_.empty()) {
|
||||
|
|
@ -264,12 +347,23 @@ void SceneManager::render(RenderBackend &renderer) {
|
|||
E2D_LOG_TRACE("SceneManager::render - endFrame");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 收集渲染命令
|
||||
* @param commands 渲染命令输出向量
|
||||
*
|
||||
* 从当前场景收集所有渲染命令
|
||||
*/
|
||||
void SceneManager::collectRenderCommands(std::vector<RenderCommand> &commands) {
|
||||
if (!sceneStack_.empty()) {
|
||||
sceneStack_.top()->collectRenderCommands(commands, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 结束场景管理器
|
||||
*
|
||||
* 清空场景栈并触发所有场景的退出回调
|
||||
*/
|
||||
void SceneManager::end() {
|
||||
while (!sceneStack_.empty()) {
|
||||
auto scene = sceneStack_.top();
|
||||
|
|
@ -280,10 +374,21 @@ void SceneManager::end() {
|
|||
namedScenes_.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除缓存的场景
|
||||
*
|
||||
* 清除命名场景缓存
|
||||
*/
|
||||
void SceneManager::purgeCachedScenes() { namedScenes_.clear(); }
|
||||
|
||||
/**
|
||||
* @brief 分发指针事件
|
||||
* @param scene 目标场景
|
||||
*
|
||||
* 处理鼠标悬停、移动、点击和滚轮事件
|
||||
*/
|
||||
void SceneManager::dispatchPointerEvents(Scene &scene) {
|
||||
auto &input = Application::instance().input();
|
||||
auto &input = Application::get().input();
|
||||
Vec2 screenPos = input.getMousePosition();
|
||||
|
||||
Vec2 worldPos = screenPos;
|
||||
|
|
|
|||
|
|
@ -7,10 +7,25 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 创建一个空的形状节点
|
||||
*/
|
||||
ShapeNode::ShapeNode() = default;
|
||||
|
||||
/**
|
||||
* @brief 创建空的形状节点
|
||||
* @return 新创建的形状节点智能指针
|
||||
*/
|
||||
Ptr<ShapeNode> ShapeNode::create() { return makePtr<ShapeNode>(); }
|
||||
|
||||
/**
|
||||
* @brief 创建点形状节点
|
||||
* @param pos 点的位置坐标
|
||||
* @param color 点的颜色
|
||||
* @return 新创建的点形状节点智能指针
|
||||
*/
|
||||
Ptr<ShapeNode> ShapeNode::createPoint(const Vec2 &pos, const Color &color) {
|
||||
auto node = makePtr<ShapeNode>();
|
||||
node->shapeType_ = ShapeType::Point;
|
||||
|
|
@ -19,6 +34,14 @@ Ptr<ShapeNode> ShapeNode::createPoint(const Vec2 &pos, const Color &color) {
|
|||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建线段形状节点
|
||||
* @param start 线段起点坐标
|
||||
* @param end 线段终点坐标
|
||||
* @param color 线段颜色
|
||||
* @param width 线段宽度
|
||||
* @return 新创建的线段形状节点智能指针
|
||||
*/
|
||||
Ptr<ShapeNode> ShapeNode::createLine(const Vec2 &start, const Vec2 &end,
|
||||
const Color &color, float width) {
|
||||
auto node = makePtr<ShapeNode>();
|
||||
|
|
@ -29,6 +52,13 @@ Ptr<ShapeNode> ShapeNode::createLine(const Vec2 &start, const Vec2 &end,
|
|||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建矩形形状节点(空心)
|
||||
* @param rect 矩形区域
|
||||
* @param color 矩形边框颜色
|
||||
* @param width 边框线宽
|
||||
* @return 新创建的矩形形状节点智能指针
|
||||
*/
|
||||
Ptr<ShapeNode> ShapeNode::createRect(const Rect &rect, const Color &color,
|
||||
float width) {
|
||||
auto node = makePtr<ShapeNode>();
|
||||
|
|
@ -42,6 +72,12 @@ Ptr<ShapeNode> ShapeNode::createRect(const Rect &rect, const Color &color,
|
|||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建填充矩形形状节点
|
||||
* @param rect 矩形区域
|
||||
* @param color 矩形填充颜色
|
||||
* @return 新创建的填充矩形形状节点智能指针
|
||||
*/
|
||||
Ptr<ShapeNode> ShapeNode::createFilledRect(const Rect &rect,
|
||||
const Color &color) {
|
||||
auto node = createRect(rect, color, 0);
|
||||
|
|
@ -49,6 +85,15 @@ Ptr<ShapeNode> ShapeNode::createFilledRect(const Rect &rect,
|
|||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建圆形形状节点(空心)
|
||||
* @param center 圆心坐标
|
||||
* @param radius 圆的半径
|
||||
* @param color 圆的边框颜色
|
||||
* @param segments 圆的分段数(边数)
|
||||
* @param width 边框线宽
|
||||
* @return 新创建的圆形形状节点智能指针
|
||||
*/
|
||||
Ptr<ShapeNode> ShapeNode::createCircle(const Vec2 ¢er, float radius,
|
||||
const Color &color, int segments,
|
||||
float width) {
|
||||
|
|
@ -64,6 +109,14 @@ Ptr<ShapeNode> ShapeNode::createCircle(const Vec2 ¢er, float radius,
|
|||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建填充圆形形状节点
|
||||
* @param center 圆心坐标
|
||||
* @param radius 圆的半径
|
||||
* @param color 圆的填充颜色
|
||||
* @param segments 圆的分段数(边数)
|
||||
* @return 新创建的填充圆形形状节点智能指针
|
||||
*/
|
||||
Ptr<ShapeNode> ShapeNode::createFilledCircle(const Vec2 ¢er, float radius,
|
||||
const Color &color, int segments) {
|
||||
auto node = createCircle(center, radius, color, segments, 0);
|
||||
|
|
@ -71,6 +124,15 @@ Ptr<ShapeNode> ShapeNode::createFilledCircle(const Vec2 ¢er, float radius,
|
|||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建三角形形状节点(空心)
|
||||
* @param p1 三角形第一个顶点坐标
|
||||
* @param p2 三角形第二个顶点坐标
|
||||
* @param p3 三角形第三个顶点坐标
|
||||
* @param color 三角形边框颜色
|
||||
* @param width 边框线宽
|
||||
* @return 新创建的三角形形状节点智能指针
|
||||
*/
|
||||
Ptr<ShapeNode> ShapeNode::createTriangle(const Vec2 &p1, const Vec2 &p2,
|
||||
const Vec2 &p3, const Color &color,
|
||||
float width) {
|
||||
|
|
@ -83,6 +145,14 @@ Ptr<ShapeNode> ShapeNode::createTriangle(const Vec2 &p1, const Vec2 &p2,
|
|||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建填充三角形形状节点
|
||||
* @param p1 三角形第一个顶点坐标
|
||||
* @param p2 三角形第二个顶点坐标
|
||||
* @param p3 三角形第三个顶点坐标
|
||||
* @param color 三角形填充颜色
|
||||
* @return 新创建的填充三角形形状节点智能指针
|
||||
*/
|
||||
Ptr<ShapeNode> ShapeNode::createFilledTriangle(const Vec2 &p1, const Vec2 &p2,
|
||||
const Vec2 &p3,
|
||||
const Color &color) {
|
||||
|
|
@ -91,6 +161,13 @@ Ptr<ShapeNode> ShapeNode::createFilledTriangle(const Vec2 &p1, const Vec2 &p2,
|
|||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建多边形形状节点(空心)
|
||||
* @param points 多边形顶点坐标数组
|
||||
* @param color 多边形边框颜色
|
||||
* @param width 边框线宽
|
||||
* @return 新创建的多边形形状节点智能指针
|
||||
*/
|
||||
Ptr<ShapeNode> ShapeNode::createPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color, float width) {
|
||||
auto node = makePtr<ShapeNode>();
|
||||
|
|
@ -102,6 +179,12 @@ Ptr<ShapeNode> ShapeNode::createPolygon(const std::vector<Vec2> &points,
|
|||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建填充多边形形状节点
|
||||
* @param points 多边形顶点坐标数组
|
||||
* @param color 多边形填充颜色
|
||||
* @return 新创建的填充多边形形状节点智能指针
|
||||
*/
|
||||
Ptr<ShapeNode> ShapeNode::createFilledPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color) {
|
||||
auto node = createPolygon(points, color, 0);
|
||||
|
|
@ -109,19 +192,36 @@ Ptr<ShapeNode> ShapeNode::createFilledPolygon(const std::vector<Vec2> &points,
|
|||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置形状的所有顶点
|
||||
* @param points 顶点坐标数组
|
||||
*/
|
||||
void ShapeNode::setPoints(const std::vector<Vec2> &points) {
|
||||
points_ = points;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加一个顶点到形状
|
||||
* @param point 要添加的顶点坐标
|
||||
*/
|
||||
void ShapeNode::addPoint(const Vec2 &point) {
|
||||
points_.push_back(point);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除所有顶点
|
||||
*/
|
||||
void ShapeNode::clearPoints() {
|
||||
points_.clear();
|
||||
}
|
||||
|
||||
Rect ShapeNode::getBoundingBox() const {
|
||||
/**
|
||||
* @brief 获取形状的边界矩形
|
||||
* @return 包围形状的轴对齐边界矩形
|
||||
*
|
||||
* 计算形状在世界坐标系中的边界框,考虑位置偏移和线宽
|
||||
*/
|
||||
Rect ShapeNode::getBounds() const {
|
||||
if (points_.empty()) {
|
||||
return Rect();
|
||||
}
|
||||
|
|
@ -163,6 +263,12 @@ Rect ShapeNode::getBoundingBox() const {
|
|||
(maxY - minY) + inflate * 2.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制形状节点
|
||||
* @param renderer 渲染后端引用
|
||||
*
|
||||
* 根据形状类型调用相应的渲染方法进行绘制
|
||||
*/
|
||||
void ShapeNode::onDraw(RenderBackend &renderer) {
|
||||
if (points_.empty()) {
|
||||
return;
|
||||
|
|
@ -245,6 +351,13 @@ void ShapeNode::onDraw(RenderBackend &renderer) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 生成渲染命令
|
||||
* @param commands 渲染命令输出向量
|
||||
* @param zOrder 渲染层级
|
||||
*
|
||||
* 根据形状类型生成对应的渲染命令并添加到命令列表
|
||||
*/
|
||||
void ShapeNode::generateRenderCommand(std::vector<RenderCommand> &commands,
|
||||
int zOrder) {
|
||||
if (points_.empty()) {
|
||||
|
|
|
|||
|
|
@ -7,10 +7,27 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*
|
||||
* 创建一个空的精灵对象
|
||||
*/
|
||||
Sprite::Sprite() = default;
|
||||
|
||||
/**
|
||||
* @brief 带纹理的构造函数
|
||||
* @param texture 精灵使用的纹理智能指针
|
||||
*
|
||||
* 创建精灵并设置纹理,纹理区域默认为整个纹理
|
||||
*/
|
||||
Sprite::Sprite(Ptr<Texture> texture) { setTexture(texture); }
|
||||
|
||||
/**
|
||||
* @brief 设置精灵纹理
|
||||
* @param texture 要设置的纹理智能指针
|
||||
*
|
||||
* 设置纹理并将纹理区域初始化为整个纹理大小
|
||||
*/
|
||||
void Sprite::setTexture(Ptr<Texture> texture) {
|
||||
texture_ = texture;
|
||||
if (texture_) {
|
||||
|
|
@ -19,27 +36,68 @@ void Sprite::setTexture(Ptr<Texture> texture) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置纹理区域
|
||||
* @param rect 纹理上的矩形区域
|
||||
*
|
||||
* 设置精灵显示纹理的哪一部分
|
||||
*/
|
||||
void Sprite::setTextureRect(const Rect &rect) { textureRect_ = rect; }
|
||||
|
||||
/**
|
||||
* @brief 设置精灵颜色
|
||||
* @param color 要设置的颜色
|
||||
*
|
||||
* 颜色会与纹理颜色混合
|
||||
*/
|
||||
void Sprite::setColor(const Color &color) { color_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置水平翻转
|
||||
* @param flip 是否水平翻转
|
||||
*/
|
||||
void Sprite::setFlipX(bool flip) { flipX_ = flip; }
|
||||
|
||||
/**
|
||||
* @brief 设置垂直翻转
|
||||
* @param flip 是否垂直翻转
|
||||
*/
|
||||
void Sprite::setFlipY(bool flip) { flipY_ = flip; }
|
||||
|
||||
/**
|
||||
* @brief 创建空精灵
|
||||
* @return 新创建的精灵智能指针
|
||||
*/
|
||||
Ptr<Sprite> Sprite::create() { return makePtr<Sprite>(); }
|
||||
|
||||
/**
|
||||
* @brief 创建带纹理的精灵
|
||||
* @param texture 精灵使用的纹理
|
||||
* @return 新创建的精灵智能指针
|
||||
*/
|
||||
Ptr<Sprite> Sprite::create(Ptr<Texture> texture) {
|
||||
return makePtr<Sprite>(texture);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建带纹理和纹理区域的精灵
|
||||
* @param texture 精灵使用的纹理
|
||||
* @param rect 纹理区域
|
||||
* @return 新创建的精灵智能指针
|
||||
*/
|
||||
Ptr<Sprite> Sprite::create(Ptr<Texture> texture, const Rect &rect) {
|
||||
auto sprite = makePtr<Sprite>(texture);
|
||||
sprite->setTextureRect(rect);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
Rect Sprite::getBoundingBox() const {
|
||||
/**
|
||||
* @brief 获取精灵的边界矩形
|
||||
* @return 精灵在世界坐标系中的轴对齐边界矩形
|
||||
*
|
||||
* 考虑位置、锚点、缩放等因素计算边界框
|
||||
*/
|
||||
Rect Sprite::getBounds() const {
|
||||
if (!texture_ || !texture_->isValid()) {
|
||||
return Rect();
|
||||
}
|
||||
|
|
@ -63,6 +121,12 @@ Rect Sprite::getBoundingBox() const {
|
|||
return Rect(l, t, std::abs(w), std::abs(h));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制精灵
|
||||
* @param renderer 渲染后端引用
|
||||
*
|
||||
* 使用世界变换计算最终位置、缩放和旋转,然后绘制精灵
|
||||
*/
|
||||
void Sprite::onDraw(RenderBackend &renderer) {
|
||||
if (!texture_ || !texture_->isValid()) {
|
||||
return;
|
||||
|
|
@ -108,6 +172,13 @@ void Sprite::onDraw(RenderBackend &renderer) {
|
|||
anchor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 生成渲染命令
|
||||
* @param commands 渲染命令输出向量
|
||||
* @param zOrder 渲染层级
|
||||
*
|
||||
* 根据精灵的纹理、变换和颜色生成精灵渲染命令
|
||||
*/
|
||||
void Sprite::generateRenderCommand(std::vector<RenderCommand> &commands,
|
||||
int zOrder) {
|
||||
if (!texture_ || !texture_->isValid()) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ std::string Logger::logFile_;
|
|||
/**
|
||||
* @brief 获取日志级别字符串
|
||||
* @param level 日志级别
|
||||
* @return 级别字符串
|
||||
* @return 级别对应的字符串表示
|
||||
*/
|
||||
const char *Logger::getLevelString(LogLevel level) {
|
||||
switch (level) {
|
||||
|
|
@ -35,6 +35,8 @@ const char *Logger::getLevelString(LogLevel level) {
|
|||
|
||||
/**
|
||||
* @brief 初始化日志系统
|
||||
*
|
||||
* 初始化日志系统并设置SDL日志级别为详细模式,允许所有级别的日志输出
|
||||
*/
|
||||
void Logger::init() {
|
||||
if (initialized_) {
|
||||
|
|
@ -50,6 +52,8 @@ void Logger::init() {
|
|||
|
||||
/**
|
||||
* @brief 关闭日志系统
|
||||
*
|
||||
* 关闭日志系统并输出关闭日志
|
||||
*/
|
||||
void Logger::shutdown() {
|
||||
if (initialized_) {
|
||||
|
|
@ -60,7 +64,9 @@ void Logger::shutdown() {
|
|||
|
||||
/**
|
||||
* @brief 设置日志级别
|
||||
* @param level 日志级别
|
||||
* @param level 要设置的日志级别
|
||||
*
|
||||
* 设置日志系统的最低输出级别,低于此级别的日志将被忽略
|
||||
*/
|
||||
void Logger::setLevel(LogLevel level) {
|
||||
level_ = level;
|
||||
|
|
@ -73,7 +79,9 @@ void Logger::setLevel(LogLevel level) {
|
|||
|
||||
/**
|
||||
* @brief 设置是否输出到控制台
|
||||
* @param enable 是否启用
|
||||
* @param enable true启用控制台输出,false禁用
|
||||
*
|
||||
* 控制日志是否输出到控制台,通过调整SDL日志优先级实现
|
||||
*/
|
||||
void Logger::setConsoleOutput(bool enable) {
|
||||
consoleOutput_ = enable;
|
||||
|
|
@ -88,7 +96,9 @@ void Logger::setConsoleOutput(bool enable) {
|
|||
|
||||
/**
|
||||
* @brief 设置日志输出到文件
|
||||
* @param filename 日志文件名
|
||||
* @param filename 日志文件路径
|
||||
*
|
||||
* 配置日志输出到指定文件,空字符串则禁用文件输出
|
||||
*/
|
||||
void Logger::setFileOutput(const std::string &filename) {
|
||||
logFile_ = filename;
|
||||
|
|
|
|||
|
|
@ -3,26 +3,54 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 构造函数,初始化随机数生成器
|
||||
*
|
||||
* 使用当前时间作为默认种子初始化随机数生成器
|
||||
*/
|
||||
Random::Random() : floatDist_(0.0f, 1.0f) {
|
||||
// 使用当前时间作为默认种子
|
||||
randomize();
|
||||
}
|
||||
|
||||
Random &Random::getInstance() {
|
||||
/**
|
||||
* @brief 获取Random单例实例
|
||||
* @return Random单例的引用
|
||||
*/
|
||||
Random &Random::get() {
|
||||
static Random instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置随机数种子
|
||||
* @param seed 随机数种子值
|
||||
*/
|
||||
void Random::setSeed(uint32 seed) { generator_.seed(seed); }
|
||||
|
||||
/**
|
||||
* @brief 使用当前时间随机化种子
|
||||
*
|
||||
* 使用高精度时钟的当前时间作为随机数生成器的种子
|
||||
*/
|
||||
void Random::randomize() {
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
auto time = now.time_since_epoch().count();
|
||||
generator_.seed(static_cast<uint32>(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取[0.0, 1.0]范围内的随机浮点数
|
||||
* @return 随机浮点数,范围[0.0, 1.0]
|
||||
*/
|
||||
float Random::getFloat() { return floatDist_(generator_); }
|
||||
|
||||
/**
|
||||
* @brief 获取指定范围内的随机浮点数
|
||||
* @param min 最小值
|
||||
* @param max 最大值
|
||||
* @return 随机浮点数,范围[min, max]
|
||||
*/
|
||||
float Random::getFloat(float min, float max) {
|
||||
if (min >= max) {
|
||||
return min;
|
||||
|
|
@ -30,6 +58,11 @@ float Random::getFloat(float min, float max) {
|
|||
return min + floatDist_(generator_) * (max - min);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取[0, max]范围内的随机整数
|
||||
* @param max 最大值(包含)
|
||||
* @return 随机整数,范围[0, max]
|
||||
*/
|
||||
int Random::getInt(int max) {
|
||||
if (max <= 0) {
|
||||
return 0;
|
||||
|
|
@ -38,6 +71,12 @@ int Random::getInt(int max) {
|
|||
return dist(generator_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定范围内的随机整数
|
||||
* @param min 最小值(包含)
|
||||
* @param max 最大值(包含)
|
||||
* @return 随机整数,范围[min, max]
|
||||
*/
|
||||
int Random::getInt(int min, int max) {
|
||||
if (min >= max) {
|
||||
return min;
|
||||
|
|
@ -46,8 +85,17 @@ int Random::getInt(int min, int max) {
|
|||
return dist(generator_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取随机布尔值(50%概率)
|
||||
* @return 随机布尔值
|
||||
*/
|
||||
bool Random::getBool() { return floatDist_(generator_) >= 0.5f; }
|
||||
|
||||
/**
|
||||
* @brief 以指定概率获取随机布尔值
|
||||
* @param probability 返回true的概率,范围[0.0, 1.0]
|
||||
* @return 随机布尔值
|
||||
*/
|
||||
bool Random::getBool(float probability) {
|
||||
if (probability <= 0.0f) {
|
||||
return false;
|
||||
|
|
@ -58,11 +106,19 @@ bool Random::getBool(float probability) {
|
|||
return floatDist_(generator_) < probability;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取随机角度值
|
||||
* @return 随机角度,范围[0, 2π]
|
||||
*/
|
||||
float Random::getAngle() {
|
||||
static const float TWO_PI = 6.28318530718f;
|
||||
return floatDist_(generator_) * TWO_PI;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取有符号随机数
|
||||
* @return 随机浮点数,范围[-1.0, 1.0]
|
||||
*/
|
||||
float Random::getSigned() { return floatDist_(generator_) * 2.0f - 1.0f; }
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -5,12 +5,23 @@ namespace extra2d {
|
|||
|
||||
uint32 Timer::nextId_ = 1;
|
||||
|
||||
/**
|
||||
* @brief 构造函数,创建定时器
|
||||
* @param interval 定时间隔(秒)
|
||||
* @param repeat 是否重复触发
|
||||
* @param callback 定时器回调函数
|
||||
*/
|
||||
Timer::Timer(float interval, bool repeat, Callback callback)
|
||||
: interval_(interval), elapsed_(0.0f), repeat_(repeat), paused_(false),
|
||||
valid_(true), callback_(std::move(callback)) {
|
||||
id_ = nextId_++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新定时器状态
|
||||
* @param deltaTime 帧间隔时间(秒)
|
||||
* @return 如果定时器触发则返回true,否则返回false
|
||||
*/
|
||||
bool Timer::update(float deltaTime) {
|
||||
if (!valid_ || paused_) {
|
||||
return false;
|
||||
|
|
@ -35,18 +46,38 @@ bool Timer::update(float deltaTime) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 重置定时器
|
||||
*
|
||||
* 重置已过时间,恢复定时器为有效且非暂停状态
|
||||
*/
|
||||
void Timer::reset() {
|
||||
elapsed_ = 0.0f;
|
||||
valid_ = true;
|
||||
paused_ = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 暂停定时器
|
||||
*/
|
||||
void Timer::pause() { paused_ = true; }
|
||||
|
||||
/**
|
||||
* @brief 恢复定时器
|
||||
*/
|
||||
void Timer::resume() { paused_ = false; }
|
||||
|
||||
/**
|
||||
* @brief 取消定时器
|
||||
*
|
||||
* 将定时器标记为无效状态
|
||||
*/
|
||||
void Timer::cancel() { valid_ = false; }
|
||||
|
||||
/**
|
||||
* @brief 获取剩余时间
|
||||
* @return 剩余时间(秒),如果定时器无效或已暂停则返回0
|
||||
*/
|
||||
float Timer::getRemaining() const {
|
||||
if (!valid_ || paused_) {
|
||||
return 0.0f;
|
||||
|
|
@ -58,6 +89,12 @@ float Timer::getRemaining() const {
|
|||
// TimerManager 实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 添加单次定时器
|
||||
* @param delay 延迟时间(秒)
|
||||
* @param callback 定时器回调函数
|
||||
* @return 定时器ID
|
||||
*/
|
||||
uint32 TimerManager::addTimer(float delay, Timer::Callback callback) {
|
||||
auto timer = std::make_unique<Timer>(delay, false, std::move(callback));
|
||||
uint32 id = timer->getId();
|
||||
|
|
@ -65,6 +102,12 @@ uint32 TimerManager::addTimer(float delay, Timer::Callback callback) {
|
|||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加重复定时器
|
||||
* @param interval 触发间隔(秒)
|
||||
* @param callback 定时器回调函数
|
||||
* @return 定时器ID
|
||||
*/
|
||||
uint32 TimerManager::addRepeatingTimer(float interval,
|
||||
Timer::Callback callback) {
|
||||
auto timer = std::make_unique<Timer>(interval, true, std::move(callback));
|
||||
|
|
@ -73,6 +116,10 @@ uint32 TimerManager::addRepeatingTimer(float interval,
|
|||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 取消指定定时器
|
||||
* @param timerId 定时器ID
|
||||
*/
|
||||
void TimerManager::cancelTimer(uint32 timerId) {
|
||||
auto it = timers_.find(timerId);
|
||||
if (it != timers_.end()) {
|
||||
|
|
@ -81,6 +128,10 @@ void TimerManager::cancelTimer(uint32 timerId) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 暂停指定定时器
|
||||
* @param timerId 定时器ID
|
||||
*/
|
||||
void TimerManager::pauseTimer(uint32 timerId) {
|
||||
auto it = timers_.find(timerId);
|
||||
if (it != timers_.end()) {
|
||||
|
|
@ -88,6 +139,10 @@ void TimerManager::pauseTimer(uint32 timerId) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 恢复指定定时器
|
||||
* @param timerId 定时器ID
|
||||
*/
|
||||
void TimerManager::resumeTimer(uint32 timerId) {
|
||||
auto it = timers_.find(timerId);
|
||||
if (it != timers_.end()) {
|
||||
|
|
@ -95,6 +150,12 @@ void TimerManager::resumeTimer(uint32 timerId) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新所有定时器
|
||||
* @param deltaTime 帧间隔时间(秒)
|
||||
*
|
||||
* 更新所有定时器状态,并移除已失效的定时器
|
||||
*/
|
||||
void TimerManager::update(float deltaTime) {
|
||||
timersToRemove_.clear();
|
||||
|
||||
|
|
@ -110,6 +171,9 @@ void TimerManager::update(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除所有定时器
|
||||
*/
|
||||
void TimerManager::clear() {
|
||||
timers_.clear();
|
||||
timersToRemove_.clear();
|
||||
|
|
|
|||
11
xmake.lua
11
xmake.lua
|
|
@ -79,14 +79,3 @@ includes("xmake/engine.lua")
|
|||
-- 定义引擎库
|
||||
define_extra2d_engine()
|
||||
|
||||
-- ==============================================
|
||||
-- 项目信息输出
|
||||
-- ==============================================
|
||||
|
||||
print("========================================")
|
||||
print("Extra2D Build Configuration")
|
||||
print("========================================")
|
||||
print("Platform: " .. target_plat)
|
||||
print("Architecture: " .. (get_config("arch") or "auto"))
|
||||
print("Mode: " .. (is_mode("debug") and "debug" or "release"))
|
||||
print("========================================")
|
||||
|
|
|
|||
Loading…
Reference in New Issue