diff --git a/Fostbite2D/include/fostbite2D/app/application.h b/Fostbite2D/include/fostbite2D/core/application.h similarity index 100% rename from Fostbite2D/include/fostbite2D/app/application.h rename to Fostbite2D/include/fostbite2D/core/application.h diff --git a/Fostbite2D/include/fostbite2D/render/camera.h b/Fostbite2D/include/fostbite2D/render/camera.h deleted file mode 100644 index 58916e9..0000000 --- a/Fostbite2D/include/fostbite2D/render/camera.h +++ /dev/null @@ -1,92 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace frostbite2D { - -// ============================================================================ -// 2D 正交相机 - 简化版本,无服务和模块依赖 -// ============================================================================ -class Camera { -public: - Camera(); - Camera(float left, float right, float bottom, float top); - Camera(const Size &viewport); - ~Camera() = default; - - // ------------------------------------------------------------------------ - // 位置和变换 - // ------------------------------------------------------------------------ - void setPosition(const Vec2 &position); - void setPosition(float x, float y); - Vec2 getPosition() const { return position_; } - - void setRotation(float degrees); - float getRotation() const { return rotation_; } - - void setZoom(float zoom); - float getZoom() const { return zoom_; } - - // ------------------------------------------------------------------------ - // 视口设置 - // ------------------------------------------------------------------------ - void setViewport(float left, float right, float bottom, float top); - void setViewport(const Rect &rect); - Rect getViewport() const; - - // ------------------------------------------------------------------------ - // 矩阵获取 - // ------------------------------------------------------------------------ - glm::mat4 getViewMatrix() const; - glm::mat4 getProjectionMatrix() const; - glm::mat4 getViewProjectionMatrix() const; - - // ------------------------------------------------------------------------ - // 坐标转换 - // ------------------------------------------------------------------------ - Vec2 screenToWorld(const Vec2 &screenPos) const; - Vec2 worldToScreen(const Vec2 &worldPos) const; - Vec2 screenToWorld(float x, float y) const; - Vec2 worldToScreen(float x, float y) const; - - // ------------------------------------------------------------------------ - // 移动相机 - // ------------------------------------------------------------------------ - void move(const Vec2 &offset); - void move(float x, float y); - - // ------------------------------------------------------------------------ - // 边界限制 - // ------------------------------------------------------------------------ - void setBounds(const Rect &bounds); - void clearBounds(); - void clampToBounds(); - - // ------------------------------------------------------------------------ - // 快捷方法:看向某点 - // ------------------------------------------------------------------------ - void lookAt(const Vec2 &target); - -private: - Vec2 position_ = Vec2::Zero(); - float rotation_ = 0.0f; - float zoom_ = 1.0f; - - float left_ = -1.0f; - float right_ = 1.0f; - float bottom_ = -1.0f; - float top_ = 1.0f; - - Rect bounds_; - bool hasBounds_ = false; - - mutable glm::mat4 viewMatrix_; - mutable glm::mat4 projMatrix_; - mutable glm::mat4 vpMatrix_; - mutable bool viewDirty_ = true; - mutable bool projDirty_ = true; -}; - -} // namespace frostbite2D diff --git a/Fostbite2D/include/fostbite2D/render/font.h b/Fostbite2D/include/fostbite2D/render/font.h deleted file mode 100644 index cd7389d..0000000 --- a/Fostbite2D/include/fostbite2D/render/font.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace frostbite2D { - -// ============================================================================ -// 字形信息 -// ============================================================================ -struct Glyph { - float u0, v0; // 纹理坐标左下角 - float u1, v1; // 纹理坐标右上角 - float width; // 字形宽度(像素) - float height; // 字形高度(像素) - float bearingX; // 水平偏移 - float bearingY; // 垂直偏移 - float advance; // 前进距离 -}; - -// ============================================================================ -// 字体图集接口 -// ============================================================================ -class FontAtlas { -public: - virtual ~FontAtlas() = default; - - /** - * @brief 获取字形信息 - * @param codepoint Unicode码点 - * @return 字形信息指针 - */ - virtual const Glyph *getGlyph(char32_t codepoint) const = 0; - - /** - * @brief 获取纹理 - * @return 纹理指针 - */ - virtual class Texture *getTexture() const = 0; - - /** - * @brief 获取字体大小 - * @return 字体大小(像素) - */ - virtual int getFontSize() const = 0; - - /** - * @brief 获取字体上升高度 - * @return 上升高度 - */ - virtual float getAscent() const = 0; - - /** - * @brief 获取字体下降高度 - * @return 下降高度 - */ - virtual float getDescent() const = 0; - - /** - * @brief 获取行间距 - * @return 行间距 - */ - virtual float getLineGap() const = 0; - - /** - * @brief 获取行高 - * @return 行高 - */ - virtual float getLineHeight() const = 0; - - /** - * @brief 计算文字尺寸 - * @param text 要测量的文本 - * @return 文本的宽度和高度 - */ - virtual Vec2 measureText(const std::string &text) = 0; - - /** - * @brief 是否支持 SDF 渲染 - * @return 支持SDF返回true - */ - virtual bool isSDF() const = 0; -}; - -} // namespace frostbite2D diff --git a/Fostbite2D/include/fostbite2D/render/opengl/gl_font_atlas.h b/Fostbite2D/include/fostbite2D/render/opengl/gl_font_atlas.h deleted file mode 100644 index c0a87b5..0000000 --- a/Fostbite2D/include/fostbite2D/render/opengl/gl_font_atlas.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace frostbite2D { - -// ============================================================================ -// OpenGL 字体图集实现 - 使用 stb_rect_pack 进行矩形打包 -// ============================================================================ -class GLFontAtlas : public FontAtlas { -public: - /** - * @brief 构造函数,从字体文件初始化字体图集 - * @param filepath 字体文件路径 - * @param fontSize 字体大小(像素) - * @param useSDF 是否使用有符号距离场渲染 - */ - GLFontAtlas(const std::string &filepath, int fontSize, bool useSDF = false); - - /** - * @brief 析构函数 - */ - ~GLFontAtlas(); - - // FontAtlas 接口实现 - const Glyph *getGlyph(char32_t codepoint) const override; - Texture *getTexture() const override { return texture_.get(); } - int getFontSize() const override { return fontSize_; } - float getAscent() const override { return ascent_; } - float getDescent() const override { return descent_; } - float getLineGap() const override { return lineGap_; } - float getLineHeight() const override { return ascent_ - descent_ + lineGap_; } - Vec2 measureText(const std::string &text) override; - bool isSDF() const override { return useSDF_; } - -private: - // 图集配置 - 增大尺寸以支持更多字符 - static constexpr int ATLAS_WIDTH = 1024; - static constexpr int ATLAS_HEIGHT = 1024; - static constexpr int PADDING = 2; // 字形之间的间距 - - int fontSize_; - bool useSDF_; - mutable std::unique_ptr texture_; - mutable std::unordered_map glyphs_; - - // stb_rect_pack 上下文 - mutable stbrp_context packContext_; - mutable std::vector packNodes_; - mutable int currentY_; - - std::vector fontData_; - stbtt_fontinfo fontInfo_; - float scale_; - float ascent_; - float descent_; - float lineGap_; - - // 预分配字形位图缓冲区,避免每次动态分配 - mutable std::vector glyphBitmapCache_; - mutable std::vector glyphRgbaCache_; - - /** - * @brief 创建字体图集纹理 - */ - void createAtlas(); - - /** - * @brief 缓存字形到图集 - * @param codepoint Unicode码点 - */ - void cacheGlyph(char32_t codepoint) const; -}; - -} // namespace frostbite2D diff --git a/Fostbite2D/include/fostbite2D/render/opengl/gl_renderer.h b/Fostbite2D/include/fostbite2D/render/opengl/gl_renderer.h deleted file mode 100644 index e7e13da..0000000 --- a/Fostbite2D/include/fostbite2D/render/opengl/gl_renderer.h +++ /dev/null @@ -1,316 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -struct SDL_Window; - -namespace frostbite2D { - -// 混合模式枚举 -enum class BlendMode { None, Alpha, Additive, Multiply }; - -// 渲染统计信息 -struct RenderStats { - uint32_t drawCalls = 0; - uint32_t triangleCount = 0; -}; - -// ============================================================================ -// OpenGL 渲染器实现 -// ============================================================================ -class GLRenderer { -public: - GLRenderer(); - ~GLRenderer(); - - /** - * @brief 初始化OpenGL渲染器 - * @param window SDL窗口指针 - * @return 初始化成功返回true,失败返回false - */ - bool init(SDL_Window *window); - - /** - * @brief 关闭渲染器,释放所有GPU资源 - */ - void shutdown(); - - /** - * @brief 开始新帧,清除颜色缓冲区并重置统计信息 - * @param clearColor 清屏颜色 - */ - void beginFrame(const Color &clearColor); - - /** - * @brief 结束当前帧,刷新所有待处理的渲染批次 - */ - void endFrame(); - - /** - * @brief 设置视口区域 - * @param x 视口左下角X坐标 - * @param y 视口左下角Y坐标 - * @param width 视口宽度 - * @param height 视口高度 - */ - void setViewport(int x, int y, int width, int height); - - /** - * @brief 设置垂直同步 - * @param enabled true启用垂直同步,false禁用 - */ - void setVSync(bool enabled); - - /** - * @brief 设置混合模式 - * @param mode 混合模式枚举值 - */ - void setBlendMode(BlendMode mode); - - /** - * @brief 设置视图投影矩阵 - * @param matrix 4x4视图投影矩阵 - */ - void setViewProjection(const glm::mat4 &matrix); - - /** - * @brief 压入变换矩阵到变换栈 - * @param transform 变换矩阵 - */ - void pushTransform(const glm::mat4 &transform); - - /** - * @brief 从变换栈弹出顶部变换矩阵 - */ - void popTransform(); - - /** - * @brief 获取当前累积的变换矩阵 - * @return 当前变换矩阵,如果栈为空则返回单位矩阵 - */ - glm::mat4 getCurrentTransform() const; - - /** - * @brief 创建纹理对象 - * @param width 纹理宽度 - * @param height 纹理高度 - * @param pixels 像素数据指针 - * @param channels 颜色通道数 - * @return 创建的纹理智能指针 - */ - Ptr createTexture(int width, int height, const uint8_t *pixels, - int channels); - - /** - * @brief 从文件加载纹理 - * @param filepath 纹理文件路径 - * @return 加载的纹理智能指针 - */ - Ptr loadTexture(const std::string &filepath); - - /** - * @brief 开始精灵批处理 - */ - void beginSpriteBatch(); - - /** - * @brief 绘制精灵(带完整参数) - * @param texture 纹理引用 - * @param destRect 目标矩形(屏幕坐标) - * @param srcRect 源矩形(纹理坐标) - * @param tint 着色颜色 - * @param rotation 旋转角度(度) - * @param anchor 锚点位置(0-1范围) - */ - void drawSprite(const Texture &texture, const Rect &destRect, - const Rect &srcRect, const Color &tint, float rotation, - const Vec2 &anchor); - - /** - * @brief 绘制精灵(简化版本) - * @param texture 纹理引用 - * @param position 绘制位置 - * @param tint 着色颜色 - */ - void drawSprite(const Texture &texture, const Vec2 &position, - const Color &tint); - - /** - * @brief 结束精灵批处理并提交绘制 - */ - void endSpriteBatch(); - - /** - * @brief 绘制线段 - * @param start 起点坐标 - * @param end 终点坐标 - * @param color 线条颜色 - * @param width 线条宽度 - */ - void drawLine(const Vec2 &start, const Vec2 &end, const Color &color, - float width); - - /** - * @brief 绘制矩形边框 - * @param rect 矩形区域 - * @param color 边框颜色 - * @param width 线条宽度 - */ - void drawRect(const Rect &rect, const Color &color, float width); - - /** - * @brief 填充矩形 - * @param rect 矩形区域 - * @param color 填充颜色 - */ - void fillRect(const Rect &rect, const Color &color); - - /** - * @brief 绘制圆形边框 - * @param center 圆心坐标 - * @param radius 半径 - * @param color 边框颜色 - * @param segments 分段数 - * @param width 线条宽度 - */ - void drawCircle(const Vec2 ¢er, float radius, const Color &color, - int segments, float width); - - /** - * @brief 填充圆形 - * @param center 圆心坐标 - * @param radius 半径 - * @param color 填充颜色 - * @param segments 分段数 - */ - void fillCircle(const Vec2 ¢er, float radius, const Color &color, - int segments); - - /** - * @brief 绘制三角形边框 - * @param p1 第一个顶点 - * @param p2 第二个顶点 - * @param p3 第三个顶点 - * @param color 边框颜色 - * @param width 线条宽度 - */ - void drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, - const Color &color, float width); - - /** - * @brief 填充三角形 - * @param p1 第一个顶点 - * @param p2 第二个顶点 - * @param p3 第三个顶点 - * @param color 填充颜色 - */ - void fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, - const Color &color); - - /** - * @brief 绘制多边形边框 - * @param points 顶点数组 - * @param color 边框颜色 - * @param width 线条宽度 - */ - void drawPolygon(const std::vector &points, const Color &color, - float width); - - /** - * @brief 填充多边形 - * @param points 顶点数组 - * @param color 填充颜色 - */ - void fillPolygon(const std::vector &points, const Color &color); - - /** - * @brief 绘制文本 - * @param font 字体图集引用 - * @param text 文本内容 - * @param position 绘制位置 - * @param color 文本颜色 - */ - void drawText(const FontAtlas &font, const std::string &text, - const Vec2 &position, const Color &color); - - /** - * @brief 绘制文本(使用浮点坐标) - * @param font 字体图集引用 - * @param text 文本内容 - * @param x X坐标 - * @param y Y坐标 - * @param color 文本颜色 - */ - void drawText(const FontAtlas &font, const std::string &text, float x, - float y, const Color &color); - - /** - * @brief 获取渲染统计信息 - * @return 渲染统计信息 - */ - RenderStats getStats() const { return stats_; } - - /** - * @brief 重置渲染统计信息 - */ - void resetStats(); - -private: - // 形状批处理常量 - static constexpr size_t MAX_CIRCLE_SEGMENTS = 128; - static constexpr size_t MAX_SHAPE_VERTICES = 8192; // 最大形状顶点数 - static constexpr size_t MAX_LINE_VERTICES = 16384; // 最大线条顶点数 - - // 形状顶点结构(包含颜色) - struct ShapeVertex { - float x, y; - float r, g, b, a; - }; - - SDL_Window *window_; - GLSpriteBatch spriteBatch_; - Ptr shapeShader_; - - GLuint shapeVao_; - GLuint shapeVbo_; - GLuint lineVao_; // 线条专用 VAO - GLuint lineVbo_; // 线条专用 VBO - - glm::mat4 viewProjection_; - std::vector transformStack_; - RenderStats stats_; - bool vsync_; - - // 形状批处理缓冲区(预分配,避免每帧内存分配) - std::array shapeVertexCache_; - size_t shapeVertexCount_ = 0; - GLenum currentShapeMode_ = GL_TRIANGLES; - - // 线条批处理缓冲区 - std::array lineVertexCache_; - size_t lineVertexCount_ = 0; - float currentLineWidth_ = 1.0f; - - // OpenGL 状态缓存 - BlendMode cachedBlendMode_ = BlendMode::None; - bool blendEnabled_ = false; - - void initShapeRendering(); - void flushShapeBatch(); - void flushLineBatch(); - void addShapeVertex(float x, float y, const Color &color); - void addLineVertex(float x, float y, const Color &color); - void submitShapeBatch(GLenum mode); -}; - -} // namespace frostbite2D diff --git a/Fostbite2D/include/fostbite2D/render/opengl/gl_shader.h b/Fostbite2D/include/fostbite2D/render/opengl/gl_shader.h deleted file mode 100644 index 9f7b9aa..0000000 --- a/Fostbite2D/include/fostbite2D/render/opengl/gl_shader.h +++ /dev/null @@ -1,194 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace frostbite2D { - -class GLShader : public IShader { -public: - /** - * @brief 构造函数 - */ - GLShader(); - - /** - * @brief 析构函数 - */ - ~GLShader() override; - - /** - * @brief 绑定Shader程序 - */ - void bind() const override; - - /** - * @brief 解绑Shader程序 - */ - void unbind() const override; - - /** - * @brief 设置布尔类型uniform变量 - * @param name uniform变量名 - * @param value 布尔值 - */ - void setBool(const std::string &name, bool value) override; - - /** - * @brief 设置整数类型uniform变量 - * @param name uniform变量名 - * @param value 整数值 - */ - void setInt(const std::string &name, int value) override; - - /** - * @brief 设置浮点类型uniform变量 - * @param name uniform变量名 - * @param value 浮点值 - */ - void setFloat(const std::string &name, float value) override; - - /** - * @brief 设置二维向量类型uniform变量 - * @param name uniform变量名 - * @param value 二维向量值 - */ - void setVec2(const std::string &name, const glm::vec2 &value) override; - - /** - * @brief 设置三维向量类型uniform变量 - * @param name uniform变量名 - * @param value 三维向量值 - */ - void setVec3(const std::string &name, const glm::vec3 &value) override; - - /** - * @brief 设置四维向量类型uniform变量 - * @param name uniform变量名 - * @param value 四维向量值 - */ - void setVec4(const std::string &name, const glm::vec4 &value) override; - - /** - * @brief 设置4x4矩阵类型uniform变量 - * @param name uniform变量名 - * @param value 4x4矩阵值 - */ - void setMat4(const std::string &name, const glm::mat4 &value) override; - - /** - * @brief 设置颜色类型uniform变量 - * @param name uniform变量名 - * @param color 颜色值 - */ - void setColor(const std::string &name, const Color &color) override; - - /** - * @brief 检查Shader是否有效 - * @return 有效返回true,否则返回false - */ - bool isValid() const override { return programID_ != 0; } - - /** - * @brief 获取原生句柄(OpenGL程序ID) - * @return OpenGL程序ID - */ - uint32_t getNativeHandle() const override { return programID_; } - - /** - * @brief 获取Shader名称 - * @return Shader名称 - */ - const std::string &getName() const override { return name_; } - - /** - * @brief 设置Shader名称 - * @param name Shader名称 - */ - void setName(const std::string &name) override { name_ = name; } - - /** - * @brief 从源码编译Shader - * @param vertexSource 顶点着色器源码 - * @param fragmentSource 片段着色器源码 - * @return 编译成功返回true,失败返回false - */ - bool compileFromSource(const char *vertexSource, const char *fragmentSource); - - /** - * @brief 从二进制数据创建Shader - * @param binary 二进制数据 - * @return 创建成功返回true,失败返回false - */ - bool compileFromBinary(const std::vector &binary); - - /** - * @brief 获取Shader二进制数据 - * @param outBinary 输出的二进制数据 - * @return 成功返回true,失败返回false - */ - bool getBinary(std::vector &outBinary); - - /** - * @brief 获取OpenGL程序ID - * @return OpenGL程序ID - */ - GLuint getProgramID() const { return programID_; } - -private: - GLuint programID_ = 0; - std::string name_; - std::unordered_map uniformCache_; - - /** - * @brief 编译单个着色器 - * @param type 着色器类型 - * @param source 着色器源码 - * @return 着色器ID,失败返回0 - */ - GLuint compileShader(GLenum type, const char *source); - - /** - * @brief 获取uniform位置 - * @param name uniform变量名 - * @return uniform位置 - */ - GLint getUniformLocation(const std::string &name); -}; - -class GLShaderFactory : public IShaderFactory { -public: - /** - * @brief 从源码创建Shader - * @param name Shader名称 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 创建的Shader实例 - */ - Ptr createFromSource(const std::string &name, - const std::string &vertSource, - const std::string &fragSource) override; - - /** - * @brief 从缓存二进制创建Shader - * @param name Shader名称 - * @param binary 编译后的二进制数据 - * @return 创建的Shader实例 - */ - Ptr createFromBinary(const std::string &name, - const std::vector &binary) override; - - /** - * @brief 获取Shader的二进制数据 - * @param shader Shader实例 - * @param outBinary 输出的二进制数据 - * @return 成功返回true,失败返回false - */ - bool getShaderBinary(const IShader &shader, - std::vector &outBinary) override; -}; - -} // namespace frostbite2D diff --git a/Fostbite2D/include/fostbite2D/render/opengl/gl_sprite_batch.h b/Fostbite2D/include/fostbite2D/render/opengl/gl_sprite_batch.h deleted file mode 100644 index 5a0b1bf..0000000 --- a/Fostbite2D/include/fostbite2D/render/opengl/gl_sprite_batch.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace frostbite2D { - -// ============================================================================ -// OpenGL 精灵批渲染器 - 优化版本 -// ============================================================================ -class GLSpriteBatch { -public: - static constexpr size_t MAX_SPRITES = 10000; - static constexpr size_t VERTICES_PER_SPRITE = 4; - static constexpr size_t INDICES_PER_SPRITE = 6; - static constexpr size_t MAX_VERTICES = MAX_SPRITES * VERTICES_PER_SPRITE; - static constexpr size_t MAX_INDICES = MAX_SPRITES * INDICES_PER_SPRITE; - - struct Vertex { - glm::vec2 position; - glm::vec2 texCoord; - glm::vec4 color; - }; - - struct SpriteData { - glm::vec2 position; - glm::vec2 size; - glm::vec2 texCoordMin; - glm::vec2 texCoordMax; - glm::vec4 color; - float rotation; - glm::vec2 anchor; - bool isSDF = false; - }; - - GLSpriteBatch(); - ~GLSpriteBatch(); - - bool init(); - void shutdown(); - - void begin(const glm::mat4 &viewProjection); - void draw(const Texture &texture, const SpriteData &data); - void end(); - - // 批量绘制接口 - 用于自动批处理 - void drawBatch(const Texture &texture, - const std::vector &sprites); - - // 立即绘制(不缓存) - void drawImmediate(const Texture &texture, const SpriteData &data); - - // 统计 - uint32_t getDrawCallCount() const { return drawCallCount_; } - uint32_t getSpriteCount() const { return spriteCount_; } - uint32_t getBatchCount() const { return batchCount_; } - - // 检查是否需要刷新 - bool needsFlush(const Texture &texture, bool isSDF) const; - -private: - GLuint vao_; - GLuint vbo_; - GLuint ibo_; - Ptr shader_; - - // 使用固定大小数组减少内存分配 - std::array vertexBuffer_; - size_t vertexCount_; - - const Texture *currentTexture_; - bool currentIsSDF_; - glm::mat4 viewProjection_; - - // 缓存上一帧的 viewProjection,避免重复设置 - glm::mat4 cachedViewProjection_; - bool viewProjectionDirty_ = true; - - uint32_t drawCallCount_; - uint32_t spriteCount_; - uint32_t batchCount_; - - void flush(); - void setupShader(); - - // 添加顶点到缓冲区 - void addVertices(const SpriteData &data); -}; - -} // namespace frostbite2D diff --git a/Fostbite2D/include/fostbite2D/render/opengl/gl_texture.h b/Fostbite2D/include/fostbite2D/render/opengl/gl_texture.h deleted file mode 100644 index 5b0b876..0000000 --- a/Fostbite2D/include/fostbite2D/render/opengl/gl_texture.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#include -#include - -namespace frostbite2D { - -// ============================================================================ -// OpenGL 纹理实现 -// ============================================================================ -class GLTexture : public Texture { -public: - GLTexture(int width, int height, const uint8_t *pixels, int channels); - GLTexture(const std::string &filepath); - ~GLTexture(); - - // Texture 接口实现 - int getWidth() const override { return width_; } - int getHeight() const override { return height_; } - Size getSize() const override { - return Size(static_cast(width_), static_cast(height_)); - } - int getChannels() const override { return channels_; } - PixelFormat getFormat() const override; - void *getNativeHandle() const override { - return reinterpret_cast(static_cast(textureID_)); - } - bool isValid() const override { return textureID_ != 0; } - void setFilter(bool linear) override; - void setWrap(bool repeat) override; - - // 从参数创建纹理的工厂方法 - static Ptr create(int width, int height, PixelFormat format); - - // 加载压缩纹理(KTX/DDS 格式) - bool loadCompressed(const std::string &filepath); - - // OpenGL 特定 - GLuint getTextureID() const { return textureID_; } - void bind(unsigned int slot = 0) const; - void unbind() const; - - // 获取纹理数据大小(字节),用于 VRAM 跟踪 - size_t getDataSize() const { return dataSize_; } - - // Alpha 遮罩 - bool hasAlphaMask() const { - return alphaMask_ != nullptr && alphaMask_->isValid(); - } - const AlphaMask *getAlphaMask() const { return alphaMask_.get(); } - void generateAlphaMask(); // 从当前纹理数据生成遮罩 - -private: - GLuint textureID_; - int width_; - int height_; - int channels_; - PixelFormat format_; - size_t dataSize_; - - // 原始像素数据(用于生成遮罩) - std::vector pixelData_; - std::unique_ptr alphaMask_; - - void createTexture(const uint8_t *pixels); - - // KTX 文件加载 - bool loadKTX(const std::string &filepath); - // DDS 文件加载 - bool loadDDS(const std::string &filepath); -}; - -} // namespace frostbite2D diff --git a/Fostbite2D/include/fostbite2D/render/shader/shader_interface.h b/Fostbite2D/include/fostbite2D/render/shader/shader_interface.h deleted file mode 100644 index 7f3f3a8..0000000 --- a/Fostbite2D/include/fostbite2D/render/shader/shader_interface.h +++ /dev/null @@ -1,151 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace frostbite2D { - -// 前向声明 -struct Color; - -// ============================================================================ -// Shader抽象接口 - 渲染后端无关 -// ============================================================================ -class IShader { -public: - virtual ~IShader() = default; - - /** - * @brief 绑定Shader程序 - */ - virtual void bind() const = 0; - - /** - * @brief 解绑Shader程序 - */ - virtual void unbind() const = 0; - - /** - * @brief 设置布尔类型uniform变量 - * @param name uniform变量名 - * @param value 布尔值 - */ - virtual void setBool(const std::string &name, bool value) = 0; - - /** - * @brief 设置整数类型uniform变量 - * @param name uniform变量名 - * @param value 整数值 - */ - virtual void setInt(const std::string &name, int value) = 0; - - /** - * @brief 设置浮点类型uniform变量 - * @param name uniform变量名 - * @param value 浮点值 - */ - virtual void setFloat(const std::string &name, float value) = 0; - - /** - * @brief 设置二维向量类型uniform变量 - * @param name uniform变量名 - * @param value 二维向量值 - */ - virtual void setVec2(const std::string &name, const glm::vec2 &value) = 0; - - /** - * @brief 设置三维向量类型uniform变量 - * @param name uniform变量名 - * @param value 三维向量值 - */ - virtual void setVec3(const std::string &name, const glm::vec3 &value) = 0; - - /** - * @brief 设置四维向量类型uniform变量 - * @param name uniform变量名 - * @param value 四维向量值 - */ - virtual void setVec4(const std::string &name, const glm::vec4 &value) = 0; - - /** - * @brief 设置4x4矩阵类型uniform变量 - * @param name uniform变量名 - * @param value 4x4矩阵值 - */ - virtual void setMat4(const std::string &name, const glm::mat4 &value) = 0; - - /** - * @brief 设置颜色类型uniform变量 - * @param name uniform变量名 - * @param color 颜色值 - */ - virtual void setColor(const std::string &name, const Color &color) = 0; - - /** - * @brief 检查Shader是否有效 - * @return 有效返回true,否则返回false - */ - virtual bool isValid() const = 0; - - /** - * @brief 获取原生句柄(如OpenGL程序ID) - * @return 原生句柄值 - */ - virtual uint32_t getNativeHandle() const = 0; - - /** - * @brief 获取Shader名称 - * @return Shader名称 - */ - virtual const std::string &getName() const = 0; - - /** - * @brief 设置Shader名称 - * @param name Shader名称 - */ - virtual void setName(const std::string &name) = 0; -}; - -// ============================================================================ -// Shader工厂接口 - 用于创建渲染后端特定的Shader实例 -// ============================================================================ -class IShaderFactory { -public: - virtual ~IShaderFactory() = default; - - /** - * @brief 从源码创建Shader - * @param name Shader名称 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 创建的Shader实例 - */ - virtual Ptr createFromSource(const std::string &name, - const std::string &vertSource, - const std::string &fragSource) = 0; - - /** - * @brief 从缓存二进制创建Shader - * @param name Shader名称 - * @param binary 编译后的二进制数据 - * @return 创建的Shader实例 - */ - virtual Ptr createFromBinary(const std::string &name, - const std::vector &binary) = 0; - - /** - * @brief 获取Shader的二进制数据(用于缓存) - * @param shader Shader实例 - * @param outBinary 输出的二进制数据 - * @return 成功返回true,失败返回false - */ - virtual bool getShaderBinary(const IShader &shader, - std::vector &outBinary) = 0; -}; - -} // namespace frostbite2D diff --git a/Fostbite2D/include/fostbite2D/render/shader/shader_manager.h b/Fostbite2D/include/fostbite2D/render/shader/shader_manager.h deleted file mode 100644 index b938ec6..0000000 --- a/Fostbite2D/include/fostbite2D/render/shader/shader_manager.h +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace frostbite2D { - -// ============================================================================ -// Shader重载回调 -// ============================================================================ -using ShaderReloadCallback = std::function newShader)>; - -// ============================================================================ -// Shader管理器 - 统一入口 -// ============================================================================ -class ShaderManager { -public: - /** - * @brief 获取单例实例 - * @return Shader管理器实例引用 - */ - static ShaderManager &getInstance(); - - // ------------------------------------------------------------------------ - // 初始化和关闭 - // ------------------------------------------------------------------------ - - /** - * @brief 初始化Shader系统 - * @param factory 渲染后端Shader工厂 - * @return 初始化成功返回true,失败返回false - */ - bool init(Ptr factory); - - /** - * @brief 关闭Shader系统 - */ - void shutdown(); - - /** - * @brief 检查是否已初始化 - * @return 已初始化返回true,否则返回false - */ - bool isInitialized() const { return initialized_; } - - // ------------------------------------------------------------------------ - // Shader加载 - // ------------------------------------------------------------------------ - - /** - * @brief 从源码加载Shader - * @param name Shader名称 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 加载的Shader实例 - */ - Ptr loadFromSource(const std::string &name, - const std::string &vertSource, - const std::string &fragSource); - - /** - * @brief 从文件加载Shader - * @param name Shader名称 - * @param vertPath 顶点着色器文件路径 - * @param fragPath 片段着色器文件路径 - * @return 加载的Shader实例,失败返回nullptr - */ - Ptr loadFromFile(const std::string &name, - const std::filesystem::path &vertPath, - const std::filesystem::path &fragPath); - - /** - * @brief 获取已加载的Shader - * @param name Shader名称 - * @return Shader实例,不存在返回nullptr - */ - Ptr get(const std::string &name) const; - - /** - * @brief 检查Shader是否存在 - * @param name Shader名称 - * @return 存在返回true,否则返回false - */ - bool has(const std::string &name) const; - - /** - * @brief 移除Shader - * @param name Shader名称 - */ - void remove(const std::string &name); - - /** - * @brief 清除所有Shader - */ - void clear(); - - /** - * @brief 注册重载回调 - * @param name Shader名称 - * @param callback 重载回调函数 - */ - void setReloadCallback(const std::string &name, - ShaderReloadCallback callback); - -private: - ShaderManager() = default; - ~ShaderManager() = default; - ShaderManager(const ShaderManager &) = delete; - ShaderManager &operator=(const ShaderManager &) = delete; - - Ptr factory_; - - struct ShaderInfo { - Ptr shader; - ShaderReloadCallback reloadCallback; - std::string vertSource; - std::string fragSource; - std::filesystem::path vertPath; - std::filesystem::path fragPath; - }; - std::unordered_map shaders_; - - bool initialized_ = false; -}; - -} // namespace frostbite2D diff --git a/Fostbite2D/include/fostbite2D/render/texture.h b/Fostbite2D/include/fostbite2D/render/texture.h deleted file mode 100644 index 189f105..0000000 --- a/Fostbite2D/include/fostbite2D/render/texture.h +++ /dev/null @@ -1,154 +0,0 @@ -#pragma once - -#include -#include - -namespace frostbite2D { - -// ============================================================================ -// 像素格式枚举 -// ============================================================================ -enum class PixelFormat { - R8, // 单通道灰度 - RG8, // 双通道 - RGB8, // RGB - RGBA8, // RGBA - R16F, // 半精度浮点单通道 - RG16F, // 半精度浮点双通道 - RGB16F, // 半精度浮点RGB - RGBA16F, // 半精度浮点RGBA - R32F, // 单精度浮点单通道 - RG32F, // 单精度浮点双通道 - RGB32F, // 单精度浮点RGB - RGBA32F, // 单精度浮点RGBA - // 压缩格式 - ETC2_RGB8, - ETC2_RGBA8, - ASTC_4x4, - ASTC_6x6, - ASTC_8x8, -}; - -// ============================================================================ -// 纹理抽象基类 -// ============================================================================ -class Texture { -public: - virtual ~Texture() = default; - - /** - * @brief 获取纹理宽度 - * @return 纹理宽度(像素) - */ - virtual int getWidth() const = 0; - - /** - * @brief 获取纹理高度 - * @return 纹理高度(像素) - */ - virtual int getHeight() const = 0; - - /** - * @brief 获取纹理尺寸 - * @return 纹理尺寸 - */ - virtual Size getSize() const = 0; - - /** - * @brief 获取颜色通道数 - * @return 颜色通道数 - */ - virtual int getChannels() const = 0; - - /** - * @brief 获取像素格式 - * @return 像素格式 - */ - virtual PixelFormat getFormat() const = 0; - - /** - * @brief 获取原生句柄 - * @return 原生句柄(如OpenGL纹理ID) - */ - virtual void *getNativeHandle() const = 0; - - /** - * @brief 检查纹理是否有效 - * @return 有效返回true,否则返回false - */ - virtual bool isValid() const = 0; - - /** - * @brief 设置纹理过滤模式 - * @param linear true使用线性过滤,false使用最近邻过滤 - */ - virtual void setFilter(bool linear) = 0; - - /** - * @brief 设置纹理环绕模式 - * @param repeat true使用重复模式,false使用边缘拉伸模式 - */ - virtual void setWrap(bool repeat) = 0; -}; - -// ============================================================================ -// Alpha遮罩 - 用于像素级碰撞检测 -// ============================================================================ -class AlphaMask { -public: - AlphaMask() = default; - AlphaMask(AlphaMask &&) = default; - AlphaMask &operator=(AlphaMask &&) = default; - - /** - * @brief 从像素数据创建Alpha遮罩 - * @param pixels 像素数据指针 - * @param width 宽度 - * @param height 高度 - * @param channels 通道数 - * @return 创建的AlphaMask - */ - static AlphaMask createFromPixels(const uint8_t *pixels, int width, - int height, int channels); - - /** - * @brief 检查遮罩是否有效 - * @return 有效返回true - */ - bool isValid() const { return !data_.empty() && width_ > 0 && height_ > 0; } - - /** - * @brief 获取指定位置的透明度 - * @param x X坐标 - * @param y Y坐标 - * @return 透明度值(0-255) - */ - uint8_t getAlpha(int x, int y) const; - - /** - * @brief 检查指定位置是否不透明 - * @param x X坐标 - * @param y Y坐标 - * @return 不透明返回true - */ - bool isOpaque(int x, int y) const; - - /** - * @brief 获取宽度 - * @return 宽度 - */ - int getWidth() const { return width_; } - - /** - * @brief 获取高度 - * @return 高度 - */ - int getHeight() const { return height_; } - -private: - std::vector data_; - int width_ = 0; - int height_ = 0; -}; - -} // namespace frostbite2D diff --git a/Fostbite2D/src/fostbite2D/app/application.cpp b/Fostbite2D/src/fostbite2D/core/application.cpp similarity index 96% rename from Fostbite2D/src/fostbite2D/app/application.cpp rename to Fostbite2D/src/fostbite2D/core/application.cpp index d2632cd..6407ce9 100644 --- a/Fostbite2D/src/fostbite2D/app/application.cpp +++ b/Fostbite2D/src/fostbite2D/core/application.cpp @@ -1,5 +1,5 @@ #include -#include +#include namespace frostbite2D { Application &Application::get() { diff --git a/Fostbite2D/src/fostbite2D/render/camera.cpp b/Fostbite2D/src/fostbite2D/render/camera.cpp deleted file mode 100644 index 769cdcd..0000000 --- a/Fostbite2D/src/fostbite2D/render/camera.cpp +++ /dev/null @@ -1,318 +0,0 @@ -#include -#include -#include -#include - -namespace frostbite2D { - -// 使用 math_types.h 中定义的 DEG_TO_RAD - -/** - * @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) {} - -/** - * @brief 设置相机位置 - * @param position 新的位置坐标 - * - * 设置相机在世界空间中的位置,会标记视图矩阵为脏 - */ -void Camera::setPosition(const Vec2 &position) { - position_ = position; - viewDirty_ = true; -} - -/** - * @brief 设置相机位置 - * @param x X坐标 - * @param y Y坐标 - * - * 设置相机在世界空间中的位置,会标记视图矩阵为脏 - */ -void Camera::setPosition(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; - bottom_ = bottom; - top_ = top; - projDirty_ = true; -} - -/** - * @brief 设置视口范围 - * @param rect 视口矩形 - * - * 使用矩形设置相机的正交投影视口范围,会标记投影矩阵为脏 - */ -void Camera::setViewport(const Rect &rect) { - left_ = rect.left(); - right_ = rect.right(); - bottom_ = rect.bottom(); - top_ = rect.top(); - projDirty_ = true; -} - -/** - * @brief 获取视口矩形 - * @return 当前视口的矩形表示 - * - * 返回当前相机的视口范围 - */ -Rect Camera::getViewport() const { - return Rect(left_, top_, right_ - left_, bottom_ - top_); -} - -/** - * @brief 获取视图矩阵 - * @return 视图矩阵 - * - * 变换顺序:平移 -> 旋转 -> 缩放(逆序应用) - * View = T(-position) × R(-rotation) × S(1/zoom) - */ -glm::mat4 Camera::getViewMatrix() const { - if (viewDirty_) { - viewMatrix_ = glm::mat4(1.0f); - - // 1. 平移(最后应用) - viewMatrix_ = glm::translate(viewMatrix_, - glm::vec3(-position_.x, -position_.y, 0.0f)); - - // 2. 旋转(中间应用) - if (rotation_ != 0.0f) { - 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)); - } - - viewDirty_ = false; - } - return viewMatrix_; -} - -/** - * @brief 获取投影矩阵 - * @return 正交投影矩阵 - * - * 对于2D游戏,Y轴向下增长(屏幕坐标系) - * OpenGL默认Y轴向上,所以需要反转Y轴 - */ -glm::mat4 Camera::getProjectionMatrix() const { - if (projDirty_) { - // 对于2D游戏,Y轴向下增长(屏幕坐标系) - // OpenGL默认Y轴向上,所以需要反转Y轴 - // glm::ortho(left, right, bottom, top) - // 为了Y轴向下:传入 bottom > top,这样Y轴翻转 - projMatrix_ = - glm::ortho(left_, right_, // X轴:从左到右 - bottom_, top_, // Y轴:从上到下(反转,实现Y轴向下增长) - -1.0f, 1.0f); - projDirty_ = false; - } - return projMatrix_; -} - -/** - * @brief 获取视图-投影矩阵 - * @return 视图-投影矩阵 - */ -glm::mat4 Camera::getViewProjectionMatrix() const { - return getProjectionMatrix() * getViewMatrix(); -} - -/** - * @brief 将屏幕坐标转换为世界坐标 - * @param screenPos 屏幕坐标 - * @return 世界坐标 - */ -Vec2 Camera::screenToWorld(const Vec2 &screenPos) const { - // 使用逆视图-投影矩阵转换 - glm::mat4 invVP = glm::inverse(getViewProjectionMatrix()); - glm::vec4 ndc(screenPos.x, screenPos.y, 0.0f, 1.0f); - glm::vec4 world = invVP * ndc; - return Vec2(world.x, world.y); -} - -/** - * @brief 将世界坐标转换为屏幕坐标 - * @param worldPos 世界坐标 - * @return 屏幕坐标 - */ -Vec2 Camera::worldToScreen(const Vec2 &worldPos) const { - glm::vec4 world(worldPos.x, worldPos.y, 0.0f, 1.0f); - glm::vec4 screen = getViewProjectionMatrix() * world; - return Vec2(screen.x, screen.y); -} - -/** - * @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; - - float viewportWidth = (right_ - left_) / zoom_; - float viewportHeight = (bottom_ - top_) / zoom_; - - float minX = bounds_.left() + viewportWidth * 0.5f; - float maxX = bounds_.right() - viewportWidth * 0.5f; - float minY = bounds_.top() + viewportHeight * 0.5f; - float maxY = bounds_.bottom() - viewportHeight * 0.5f; - - if (minX > maxX) { - position_.x = bounds_.center().x; - } else { - position_.x = std::clamp(position_.x, minX, maxX); - } - - if (minY > maxY) { - position_.y = bounds_.center().y; - } else { - position_.y = std::clamp(position_.y, minY, maxY); - } - - viewDirty_ = true; -} - -/** - * @brief 将相机移动到目标位置 - * @param target 目标位置 - * - * 设置相机位置到指定的世界坐标 - */ -void Camera::lookAt(const Vec2 &target) { - position_ = target; - viewDirty_ = true; -} - -} // namespace frostbite2D diff --git a/Fostbite2D/src/fostbite2D/render/opengl/gl_font_atlas.cpp b/Fostbite2D/src/fostbite2D/render/opengl/gl_font_atlas.cpp deleted file mode 100644 index 90c2f2b..0000000 --- a/Fostbite2D/src/fostbite2D/render/opengl/gl_font_atlas.cpp +++ /dev/null @@ -1,313 +0,0 @@ -#include -#include -#include -#define STB_TRUETYPE_IMPLEMENTATION -#include -#define STB_RECT_PACK_IMPLEMENTATION -#include -#include - -namespace frostbite2D { - -// ============================================================================ -// 构造函数 - 初始化字体图集 -// ============================================================================ -/** - * @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) { - - // 加载字体文件 - std::ifstream file(filepath, std::ios::binary | std::ios::ate); - if (!file.is_open()) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load font: %s", - filepath.c_str()); - return; - } - - std::streamsize size = file.tellg(); - file.seekg(0, std::ios::beg); - fontData_.resize(size); - if (!file.read(reinterpret_cast(fontData_.data()), size)) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to read font file: %s", - filepath.c_str()); - return; - } - - // 初始化 stb_truetype - if (!stbtt_InitFont(&fontInfo_, fontData_.data(), - stbtt_GetFontOffsetForIndex(fontData_.data(), 0))) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to init font: %s", - filepath.c_str()); - return; - } - - scale_ = stbtt_ScaleForPixelHeight(&fontInfo_, static_cast(fontSize_)); - - int ascent, descent, lineGap; - stbtt_GetFontVMetrics(&fontInfo_, &ascent, &descent, &lineGap); - ascent_ = static_cast(ascent) * scale_; - descent_ = static_cast(descent) * scale_; - lineGap_ = static_cast(lineGap) * scale_; - - createAtlas(); -} - -// ============================================================================ -// 析构函数 -// ============================================================================ -/** - * @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()) { - cacheGlyph(codepoint); - it = glyphs_.find(codepoint); - } - return (it != glyphs_.end()) ? &it->second : nullptr; -} - -// ============================================================================ -// 测量文本尺寸 -// ============================================================================ -/** - * @brief 测量文本渲染后的尺寸 - * @param text 要测量的文本 - * @return 文本的宽度和高度 - */ -Vec2 GLFontAtlas::measureText(const std::string &text) { - float width = 0.0f; - float height = getAscent() - getDescent(); - float currentWidth = 0.0f; - - for (char c : text) { - char32_t codepoint = static_cast(static_cast(c)); - if (codepoint == '\n') { - width = std::max(width, currentWidth); - currentWidth = 0.0f; - height += getLineHeight(); - continue; - } - - const Glyph *glyph = getGlyph(codepoint); - if (glyph) { - currentWidth += glyph->advance; - } - } - - width = std::max(width, currentWidth); - return Vec2(width, height); -} - -// ============================================================================ -// 创建图集纹理 - 初始化空白纹理和矩形打包上下文 -// ============================================================================ -/** - * @brief 创建字体图集纹理,初始化空白纹理和矩形打包上下文 - */ -void GLFontAtlas::createAtlas() { - // 统一使用 4 通道格式 - int channels = 4; - std::vector emptyData(ATLAS_WIDTH * ATLAS_HEIGHT * channels, 0); - texture_ = std::make_unique(ATLAS_WIDTH, ATLAS_HEIGHT, - emptyData.data(), channels); - texture_->setFilter(true); - - // 初始化矩形打包上下文 - packNodes_.resize(ATLAS_WIDTH); - stbrp_init_target(&packContext_, ATLAS_WIDTH, ATLAS_HEIGHT, packNodes_.data(), - ATLAS_WIDTH); - - // 预分配字形缓冲区 - // 假设最大字形尺寸为 fontSize * fontSize * 4 (RGBA) - size_t maxGlyphSize = static_cast(fontSize_ * fontSize_ * 4 * 4); - glyphBitmapCache_.reserve(maxGlyphSize); - glyphRgbaCache_.reserve(maxGlyphSize); -} - -// ============================================================================ -// 缓存字形 - 渲染字形到图集并存储信息 -// 使用 stb_rect_pack 进行矩形打包 -// ============================================================================ -/** - * @brief 缓存字形到图集,渲染字形位图并存储字形信息 - * @param codepoint Unicode码点 - */ -void GLFontAtlas::cacheGlyph(char32_t codepoint) const { - int advance = 0; - stbtt_GetCodepointHMetrics(&fontInfo_, static_cast(codepoint), &advance, - nullptr); - float advancePx = advance * scale_; - - if (useSDF_) { - constexpr int SDF_PADDING = 8; - constexpr unsigned char ONEDGE_VALUE = 128; - constexpr float PIXEL_DIST_SCALE = 64.0f; - - int w = 0, h = 0, xoff = 0, yoff = 0; - unsigned char *sdf = stbtt_GetCodepointSDF( - &fontInfo_, scale_, static_cast(codepoint), SDF_PADDING, - ONEDGE_VALUE, PIXEL_DIST_SCALE, &w, &h, &xoff, &yoff); - if (!sdf || w <= 0 || h <= 0) { - if (sdf) - stbtt_FreeSDF(sdf, nullptr); - Glyph glyph{}; - glyph.advance = advancePx; - glyphs_[codepoint] = glyph; - return; - } - - stbrp_rect rect; - rect.id = static_cast(codepoint); - rect.w = w + PADDING * 2; - rect.h = h + PADDING * 2; - - stbrp_pack_rects(&packContext_, &rect, 1); - if (!rect.was_packed) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Font atlas is full, cannot cache codepoint: %d", - static_cast(codepoint)); - stbtt_FreeSDF(sdf, nullptr); - return; - } - - int atlasX = rect.x + PADDING; - int atlasY = rect.y + PADDING; - - Glyph glyph; - glyph.width = static_cast(w); - glyph.height = static_cast(h); - glyph.bearingX = static_cast(xoff); - glyph.bearingY = static_cast(yoff); - glyph.advance = advancePx; - - // stb_rect_pack 使用左上角为原点,OpenGL纹理使用左下角为原点 - // 需要翻转V坐标 - float v0 = static_cast(atlasY) / ATLAS_HEIGHT; - float v1 = static_cast(atlasY + h) / ATLAS_HEIGHT; - glyph.u0 = static_cast(atlasX) / ATLAS_WIDTH; - glyph.v0 = 1.0f - v1; // 翻转V坐标 - glyph.u1 = static_cast(atlasX + w) / ATLAS_WIDTH; - glyph.v1 = 1.0f - v0; // 翻转V坐标 - - glyphs_[codepoint] = glyph; - - // 将 SDF 单通道数据转换为 RGBA 格式(统一格式) - size_t pixelCount = static_cast(w) * static_cast(h); - glyphRgbaCache_.resize(pixelCount * 4); - for (size_t i = 0; i < pixelCount; ++i) { - uint8_t alpha = sdf[i]; - glyphRgbaCache_[i * 4 + 0] = 255; // R - glyphRgbaCache_[i * 4 + 1] = 255; // G - glyphRgbaCache_[i * 4 + 2] = 255; // B - glyphRgbaCache_[i * 4 + 3] = alpha; // A - SDF 值存储在 Alpha 通道 - } - - // 直接设置像素对齐为 4,无需查询当前状态 - glBindTexture(GL_TEXTURE_2D, texture_->getTextureID()); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - // OpenGL纹理坐标原点在左下角,需要将Y坐标翻转 - glTexSubImage2D(GL_TEXTURE_2D, 0, atlasX, ATLAS_HEIGHT - atlasY - h, w, h, - GL_RGBA, GL_UNSIGNED_BYTE, glyphRgbaCache_.data()); - - stbtt_FreeSDF(sdf, nullptr); - return; - } - - int x0 = 0, y0 = 0, x1 = 0, y1 = 0; - stbtt_GetCodepointBitmapBox(&fontInfo_, static_cast(codepoint), scale_, - scale_, &x0, &y0, &x1, &y1); - int w = x1 - x0; - int h = y1 - y0; - int xoff = x0; - int yoff = y0; - - if (w <= 0 || h <= 0) { - Glyph glyph{}; - glyph.advance = advancePx; - glyphs_[codepoint] = glyph; - return; - } - - // 使用预分配缓冲区 - size_t pixelCount = static_cast(w) * static_cast(h); - glyphBitmapCache_.resize(pixelCount); - stbtt_MakeCodepointBitmap(&fontInfo_, glyphBitmapCache_.data(), w, h, w, - scale_, scale_, static_cast(codepoint)); - - // 使用 stb_rect_pack 打包矩形 - stbrp_rect rect; - rect.id = static_cast(codepoint); - rect.w = w + PADDING * 2; - rect.h = h + PADDING * 2; - - stbrp_pack_rects(&packContext_, &rect, 1); - - if (!rect.was_packed) { - // 图集已满,无法缓存更多字形 - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Font atlas is full, cannot cache codepoint: %d", - static_cast(codepoint)); - return; - } - - int atlasX = rect.x + PADDING; - int atlasY = rect.y + PADDING; - - // 创建字形信息 - Glyph glyph; - glyph.width = static_cast(w); - glyph.height = static_cast(h); - glyph.bearingX = static_cast(xoff); - glyph.bearingY = static_cast(yoff); - glyph.advance = advancePx; - - // 计算纹理坐标(相对于图集) - // stb_rect_pack 使用左上角为原点,OpenGL纹理使用左下角为原点 - // 需要翻转V坐标 - float v0 = static_cast(atlasY) / ATLAS_HEIGHT; - float v1 = static_cast(atlasY + h) / ATLAS_HEIGHT; - glyph.u0 = static_cast(atlasX) / ATLAS_WIDTH; - glyph.v0 = 1.0f - v1; // 翻转V坐标 - glyph.u1 = static_cast(atlasX + w) / ATLAS_WIDTH; - glyph.v1 = 1.0f - v0; // 翻转V坐标 - - // 存储字形 - glyphs_[codepoint] = glyph; - - // 将单通道字形数据转换为 RGBA 格式(白色字形,Alpha 通道存储灰度) - glyphRgbaCache_.resize(pixelCount * 4); - for (size_t i = 0; i < pixelCount; ++i) { - uint8_t alpha = glyphBitmapCache_[i]; - glyphRgbaCache_[i * 4 + 0] = 255; // R - glyphRgbaCache_[i * 4 + 1] = 255; // G - glyphRgbaCache_[i * 4 + 2] = 255; // B - glyphRgbaCache_[i * 4 + 3] = alpha; // A - } - - // 更新纹理 - 将字形数据上传到图集的指定位置 - // 直接设置像素对齐为 4,无需查询当前状态 - glBindTexture(GL_TEXTURE_2D, texture_->getTextureID()); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - // OpenGL纹理坐标原点在左下角,需要将Y坐标翻转 - glTexSubImage2D(GL_TEXTURE_2D, 0, atlasX, ATLAS_HEIGHT - atlasY - h, w, h, - GL_RGBA, GL_UNSIGNED_BYTE, glyphRgbaCache_.data()); -} - -} // namespace frostbite2D diff --git a/Fostbite2D/src/fostbite2D/render/opengl/gl_renderer.cpp b/Fostbite2D/src/fostbite2D/render/opengl/gl_renderer.cpp deleted file mode 100644 index 8b92737..0000000 --- a/Fostbite2D/src/fostbite2D/render/opengl/gl_renderer.cpp +++ /dev/null @@ -1,802 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -namespace frostbite2D { - -// VBO 初始大小(用于 VRAM 跟踪) -static constexpr size_t SHAPE_VBO_SIZE = 1024 * sizeof(float); - -// ============================================================================ -// BlendMode 查找表 - 编译期构建,运行时 O(1) 查找 -// ============================================================================ -struct BlendState { - bool enable; - GLenum srcFactor; - GLenum dstFactor; -}; - -static constexpr BlendState BLEND_STATES[] = { - {false, 0, 0}, // BlendMode::None - {true, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}, // BlendMode::Alpha - {true, GL_SRC_ALPHA, GL_ONE}, // BlendMode::Additive - {true, GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA} // BlendMode::Multiply -}; - -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), - lineVertexCount_(0), currentLineWidth_(1.0f) { - resetStats(); - for (auto &v : shapeVertexCache_) { - v = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; - } - for (auto &v : lineVertexCache_) { - v = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; - } -} - -/** - * @brief 析构函数,调用shutdown释放资源 - */ -GLRenderer::~GLRenderer() { shutdown(); } - -/** - * @brief 初始化OpenGL渲染器 - * @param window SDL窗口指针 - * @return 初始化成功返回true,失败返回false - */ -bool GLRenderer::init(SDL_Window *window) { - window_ = window; - - // Switch: GL 上下文已通过 SDL2 + EGL 初始化,无需 glewInit() - - // 初始化精灵批渲染器 - if (!spriteBatch_.init()) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Failed to initialize sprite batch"); - return false; - } - - // 初始化形状渲染 - initShapeRendering(); - - // 设置 OpenGL 状态 - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "OpenGL Renderer initialized"); - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "OpenGL Version: %s", - reinterpret_cast(glGetString(GL_VERSION))); - - return true; -} - -/** - * @brief 关闭渲染器,释放所有GPU资源 - */ -void GLRenderer::shutdown() { - spriteBatch_.shutdown(); - - if (lineVbo_ != 0) { - glDeleteBuffers(1, &lineVbo_); - lineVbo_ = 0; - } - if (lineVao_ != 0) { - glDeleteVertexArrays(1, &lineVao_); - lineVao_ = 0; - } - if (shapeVbo_ != 0) { - glDeleteBuffers(1, &shapeVbo_); - shapeVbo_ = 0; - } - if (shapeVao_ != 0) { - glDeleteVertexArrays(1, &shapeVao_); - shapeVao_ = 0; - } -} - -/** - * @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(); - // 刷新所有待处理的线条批次 - 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; - // 通过SDL设置垂直同步 - SDL_GL_SetSwapInterval(enabled ? 1 : 0); -} - -/** - * @brief 设置混合模式 - * @param mode 混合模式枚举值 - */ -void GLRenderer::setBlendMode(BlendMode mode) { - // 状态缓存检查,避免冗余 GL 调用 - if (cachedBlendMode_ == mode) { - return; - } - cachedBlendMode_ = mode; - - // 使用查找表替代 switch - size_t index = static_cast(mode); - if (index >= BLEND_STATE_COUNT) { - index = 0; - } - - const BlendState &state = BLEND_STATES[index]; - if (state.enable) { - if (!blendEnabled_) { - glEnable(GL_BLEND); - blendEnabled_ = true; - } - glBlendFunc(state.srcFactor, state.dstFactor); - } else { - if (blendEnabled_) { - glDisable(GL_BLEND); - blendEnabled_ = false; - } - } -} - -/** - * @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); - } else { - transformStack_.push_back(transformStack_.back() * 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); - } - return transformStack_.back(); -} - -/** - * @brief 创建纹理对象 - * @param width 纹理宽度 - * @param height 纹理高度 - * @param pixels 像素数据指针 - * @param channels 颜色通道数 - * @return 创建的纹理智能指针 - */ -Ptr GLRenderer::createTexture(int width, int height, - const uint8_t *pixels, int channels) { - return makePtr(width, height, pixels, channels); -} - -/** - * @brief 从文件加载纹理 - * @param filepath 纹理文件路径 - * @return 加载的纹理智能指针 - */ -Ptr GLRenderer::loadTexture(const std::string &filepath) { - return makePtr(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) { - GLSpriteBatch::SpriteData data; - data.position = glm::vec2(destRect.origin.x, destRect.origin.y); - data.size = glm::vec2(destRect.size.width, destRect.size.height); - - Texture *tex = const_cast(&texture); - float texW = static_cast(tex->getWidth()); - float texH = static_cast(tex->getHeight()); - - // 纹理坐标计算 - float u1 = srcRect.origin.x / texW; - float u2 = (srcRect.origin.x + srcRect.size.width) / texW; - float v1 = srcRect.origin.y / texH; - float v2 = (srcRect.origin.y + srcRect.size.height) / texH; - - data.texCoordMin = glm::vec2(glm::min(u1, u2), glm::min(v1, v2)); - data.texCoordMax = glm::vec2(glm::max(u1, u2), glm::max(v1, v2)); - - data.color = glm::vec4(tint.r, tint.g, tint.b, tint.a); - data.rotation = rotation * 3.14159f / 180.0f; - data.anchor = glm::vec2(anchor.x, anchor.y); - data.isSDF = false; - - 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(texture.getWidth()), - static_cast(texture.getHeight())); - Rect srcRect(0, 0, static_cast(texture.getWidth()), - static_cast(texture.getHeight())); - 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) { - // 如果线宽改变,需要先刷新线条批次 - if (width != currentLineWidth_) { - flushLineBatch(); - currentLineWidth_ = width; - } - - // 添加两个顶点到线条缓冲区 - addLineVertex(start.x, start.y, color); - addLineVertex(end.x, end.y, color); -} - -void GLRenderer::drawRect(const Rect &rect, const Color &color, float width) { - // 如果线宽改变,需要先刷新线条批次 - if (width != currentLineWidth_) { - flushLineBatch(); - currentLineWidth_ = width; - } - - float x1 = rect.origin.x; - float y1 = rect.origin.y; - float x2 = rect.origin.x + rect.size.width; - float y2 = rect.origin.y + rect.size.height; - - // 4条线段 = 8个顶点 - // 上边 - addLineVertex(x1, y1, color); - addLineVertex(x2, y1, color); - // 右边 - addLineVertex(x2, y1, color); - addLineVertex(x2, y2, color); - // 下边 - addLineVertex(x2, y2, color); - addLineVertex(x1, y2, color); - // 左边 - addLineVertex(x1, y2, color); - addLineVertex(x1, y1, color); -} - -/** - * @brief 填充矩形 - * @param rect 矩形区域 - * @param color 填充颜色 - */ -void GLRenderer::fillRect(const Rect &rect, const Color &color) { - // 提交当前批次(如果模式不同) - submitShapeBatch(GL_TRIANGLES); - - // 添加两个三角形组成矩形(6个顶点) - float x1 = rect.origin.x; - float y1 = rect.origin.y; - float x2 = rect.origin.x + rect.size.width; - float y2 = rect.origin.y + rect.size.height; - - // 三角形1: (x1,y1), (x2,y1), (x2,y2) - addShapeVertex(x1, y1, color); - addShapeVertex(x2, y1, color); - addShapeVertex(x2, y2, color); - - // 三角形2: (x1,y1), (x2,y2), (x1,y2) - addShapeVertex(x1, y1, color); - addShapeVertex(x2, y2, 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) { - // 限制段数不超过缓存大小 - if (segments > static_cast(MAX_CIRCLE_SEGMENTS)) { - segments = static_cast(MAX_CIRCLE_SEGMENTS); - } - - // 如果线宽改变,需要先刷新线条批次 - if (width != currentLineWidth_) { - flushLineBatch(); - currentLineWidth_ = width; - } - - // 使用线条批处理绘制圆形 - for (int i = 0; i < segments; ++i) { - float angle1 = - 2.0f * 3.14159f * static_cast(i) / static_cast(segments); - float angle2 = 2.0f * 3.14159f * static_cast(i + 1) / - static_cast(segments); - - addLineVertex(center.x + radius * cosf(angle1), - center.y + radius * sinf(angle1), color); - addLineVertex(center.x + radius * cosf(angle2), - center.y + radius * sinf(angle2), color); - } -} - -/** - * @brief 填充圆形 - * @param center 圆心坐标 - * @param radius 半径 - * @param color 填充颜色 - * @param segments 分段数 - */ -void GLRenderer::fillCircle(const Vec2 ¢er, float radius, - const Color &color, int segments) { - // 限制段数不超过缓存大小 - if (segments > static_cast(MAX_CIRCLE_SEGMENTS)) { - segments = static_cast(MAX_CIRCLE_SEGMENTS); - } - - // 提交当前批次(如果模式不同) - submitShapeBatch(GL_TRIANGLES); - - // 使用三角形扇形填充圆 - // 中心点 + 边缘点 - for (int i = 0; i < segments; ++i) { - float angle1 = - 2.0f * 3.14159f * static_cast(i) / static_cast(segments); - float angle2 = 2.0f * 3.14159f * static_cast(i + 1) / - static_cast(segments); - - // 每个三角形:中心 -> 边缘点1 -> 边缘点2 - addShapeVertex(center.x, center.y, color); - addShapeVertex(center.x + radius * cosf(angle1), - center.y + radius * sinf(angle1), color); - addShapeVertex(center.x + radius * cosf(angle2), - center.y + radius * sinf(angle2), color); - } -} - -/** - * @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); - drawLine(p2, p3, color, width); - 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); - - addShapeVertex(p1.x, p1.y, color); - addShapeVertex(p2.x, p2.y, color); - addShapeVertex(p3.x, p3.y, color); -} - -/** - * @brief 绘制多边形边框 - * @param points 顶点数组 - * @param color 边框颜色 - * @param width 线条宽度 - */ -void GLRenderer::drawPolygon(const std::vector &points, - const Color &color, float width) { - if (points.size() < 2) - return; - - // 如果线宽改变,需要先刷新线条批次 - if (width != currentLineWidth_) { - flushLineBatch(); - currentLineWidth_ = width; - } - - // 绘制所有边 - for (size_t i = 0; i < points.size(); ++i) { - const Vec2 &p1 = points[i]; - const Vec2 &p2 = points[(i + 1) % points.size()]; - addLineVertex(p1.x, p1.y, color); - addLineVertex(p2.x, p2.y, color); - } -} - -/** - * @brief 填充多边形 - * @param points 顶点数组 - * @param color 填充颜色 - */ -void GLRenderer::fillPolygon(const std::vector &points, - const Color &color) { - if (points.size() < 3) - return; - - submitShapeBatch(GL_TRIANGLES); - - // 使用三角形扇形填充 - // 从第一个点开始,每两个相邻点组成一个三角形 - for (size_t i = 1; i < points.size() - 1; ++i) { - addShapeVertex(points[0].x, points[0].y, color); - addShapeVertex(points[i].x, points[i].y, color); - addShapeVertex(points[i + 1].x, points[i + 1].y, color); - } -} - -/** - * @brief 重置渲染统计信息 - */ -void GLRenderer::resetStats() { stats_ = RenderStats{}; } - -/** - * @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 文本颜色 - * - * 注意:此方法需要在 beginSpriteBatch() 和 endSpriteBatch() 之间调用 - */ -void GLRenderer::drawText(const FontAtlas &font, const std::string &text, - float x, float y, const Color &color) { - // 获取字体纹理 - Texture *texture = font.getTexture(); - if (!texture) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "drawText: font texture is null"); - return; - } - - // 调试日志 - static bool firstCall = true; - if (firstCall) { - SDL_Log("drawText called, texture size: %dx%d", texture->getWidth(), texture->getHeight()); - SDL_Log("Font ascent: %f, line height: %f", font.getAscent(), font.getLineHeight()); - firstCall = false; - } - - float cursorX = x; - float cursorY = y; - float baselineY = cursorY + font.getAscent(); - int charCount = 0; - - for (char c : text) { - char32_t codepoint = static_cast(static_cast(c)); - if (codepoint == '\n') { - cursorX = x; - cursorY += font.getLineHeight(); - baselineY = cursorY + font.getAscent(); - continue; - } - - const Glyph *glyph = font.getGlyph(codepoint); - if (glyph) { - float penX = cursorX; - cursorX += glyph->advance; - - if (glyph->width <= 0.0f || glyph->height <= 0.0f) { - continue; - } - - // 计算字形位置(与 Extra2D-dev 一致) - float xPos = penX + glyph->bearingX; - float yPos = baselineY + glyph->bearingY; // 注意是 +bearingY - - GLSpriteBatch::SpriteData data; - data.position = glm::vec2(xPos, yPos); - data.size = glm::vec2(glyph->width, glyph->height); - data.texCoordMin = glm::vec2(glyph->u0, glyph->v0); - data.texCoordMax = glm::vec2(glyph->u1, glyph->v1); - data.color = glm::vec4(color.r, color.g, color.b, color.a); - data.rotation = 0.0f; - data.anchor = glm::vec2(0.0f, 0.0f); // 锚点在左上角 - data.isSDF = font.isSDF(); - - // 调试第一个字符 - if (charCount == 0) { - SDL_Log("First char: '%c' at (%.1f, %.1f), size: %.1fx%.1f, tex: (%.2f,%.2f)-(%.2f,%.2f)", - c, xPos, yPos, glyph->width, glyph->height, - glyph->u0, glyph->v0, glyph->u1, glyph->v1); - } - - spriteBatch_.draw(*texture, data); - charCount++; - } else { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Glyph not found for codepoint: %d", codepoint); - } - } - - if (charCount > 0) { - SDL_Log("drawText rendered %d characters", charCount); - } -} - -/** - * @brief 初始化形状渲染所需的OpenGL资源(VAO、VBO、着色器) - */ -void GLRenderer::initShapeRendering() { - // 从文件加载形状着色器 - shapeShader_ = ShaderManager::getInstance().loadFromFile( - "shape", "shaders/shape.vert", "shaders/shape.frag"); - if (!shapeShader_) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Failed to load shape shader from files"); - } - - // 创建形状 VAO 和 VBO - glGenVertexArrays(1, &shapeVao_); - glGenBuffers(1, &shapeVbo_); - - glBindVertexArray(shapeVao_); - glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_); - glBufferData(GL_ARRAY_BUFFER, MAX_SHAPE_VERTICES * sizeof(ShapeVertex), - nullptr, GL_DYNAMIC_DRAW); - - // 位置属性 (location = 0) - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex), - reinterpret_cast(offsetof(ShapeVertex, x))); - - // 颜色属性 (location = 1) - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex), - reinterpret_cast(offsetof(ShapeVertex, r))); - - glBindVertexArray(0); - - // 创建线条专用 VAO 和 VBO - glGenVertexArrays(1, &lineVao_); - glGenBuffers(1, &lineVbo_); - - glBindVertexArray(lineVao_); - glBindBuffer(GL_ARRAY_BUFFER, lineVbo_); - glBufferData(GL_ARRAY_BUFFER, MAX_LINE_VERTICES * sizeof(ShapeVertex), - nullptr, GL_DYNAMIC_DRAW); - - // 位置属性 (location = 0) - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex), - reinterpret_cast(offsetof(ShapeVertex, x))); - - // 颜色属性 (location = 1) - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex), - reinterpret_cast(offsetof(ShapeVertex, r))); - - glBindVertexArray(0); -} - -/** - * @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(); - } - - glm::vec4 pos(x, y, 0.0f, 1.0f); - if (!transformStack_.empty()) { - pos = transformStack_.back() * pos; - } - - ShapeVertex &v = shapeVertexCache_[shapeVertexCount_++]; - v.x = pos.x; - v.y = pos.y; - v.r = color.r; - v.g = color.g; - v.b = color.b; - 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(); - } - - glm::vec4 pos(x, y, 0.0f, 1.0f); - if (!transformStack_.empty()) { - pos = transformStack_.back() * pos; - } - - ShapeVertex &v = lineVertexCache_[lineVertexCount_++]; - v.x = pos.x; - v.y = pos.y; - v.r = color.r; - v.g = color.g; - v.b = color.b; - v.a = color.a; -} - -/** - * @brief 提交形状批次(如果需要切换绘制模式) - * @param mode OpenGL绘制模式 - */ -void GLRenderer::submitShapeBatch(GLenum mode) { - if (shapeVertexCount_ == 0) - return; - - // 如果模式改变,先刷新 - if (currentShapeMode_ != mode && shapeVertexCount_ > 0) { - flushShapeBatch(); - } - currentShapeMode_ = mode; -} - -/** - * @brief 刷新形状批次,执行实际的OpenGL绘制调用 - */ -void GLRenderer::flushShapeBatch() { - if (shapeVertexCount_ == 0) - return; - - if (shapeShader_) { - shapeShader_->bind(); - shapeShader_->setMat4("u_viewProjection", viewProjection_); - } - - glBindBuffer(GL_ARRAY_BUFFER, shapeVbo_); - glBufferSubData(GL_ARRAY_BUFFER, 0, shapeVertexCount_ * sizeof(ShapeVertex), - shapeVertexCache_.data()); - - glBindVertexArray(shapeVao_); - glDrawArrays(currentShapeMode_, 0, static_cast(shapeVertexCount_)); - - stats_.drawCalls++; - stats_.triangleCount += static_cast(shapeVertexCount_ / 3); - - shapeVertexCount_ = 0; -} - -/** - * @brief 刷新线条批次,执行实际的OpenGL绘制调用 - */ -void GLRenderer::flushLineBatch() { - if (lineVertexCount_ == 0) - return; - - // 先刷新形状批次 - flushShapeBatch(); - - glLineWidth(currentLineWidth_); - if (shapeShader_) { - shapeShader_->bind(); - shapeShader_->setMat4("u_viewProjection", viewProjection_); - } - - glBindBuffer(GL_ARRAY_BUFFER, lineVbo_); - glBufferSubData(GL_ARRAY_BUFFER, 0, lineVertexCount_ * sizeof(ShapeVertex), - lineVertexCache_.data()); - - glBindVertexArray(lineVao_); - glDrawArrays(GL_LINES, 0, static_cast(lineVertexCount_)); - - stats_.drawCalls++; - - lineVertexCount_ = 0; -} - -} // namespace frostbite2D diff --git a/Fostbite2D/src/fostbite2D/render/opengl/gl_shader.cpp b/Fostbite2D/src/fostbite2D/render/opengl/gl_shader.cpp deleted file mode 100644 index 58e902b..0000000 --- a/Fostbite2D/src/fostbite2D/render/opengl/gl_shader.cpp +++ /dev/null @@ -1,355 +0,0 @@ -#include -#include - -namespace frostbite2D { - -/** - * @brief 构造函数,初始化着色器程序ID为0 - */ -GLShader::GLShader() : programID_(0) {} - -/** - * @brief 析构函数,删除OpenGL着色器程序 - */ -GLShader::~GLShader() { - if (programID_ != 0) { - glDeleteProgram(programID_); - programID_ = 0; - } -} - -/** - * @brief 绑定Shader程序 - */ -void GLShader::bind() const { glUseProgram(programID_); } - -/** - * @brief 解绑Shader程序 - */ -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 设置颜色类型uniform变量 - * @param name uniform变量名 - * @param color 颜色值 - */ -void GLShader::setColor(const std::string &name, const Color &color) { - glUniform4f(getUniformLocation(name), color.r, color.g, color.b, color.a); -} - -/** - * @brief 从源码编译Shader - * @param vertexSource 顶点着色器源码 - * @param fragmentSource 片段着色器源码 - * @return 编译成功返回true,失败返回false - */ -bool GLShader::compileFromSource(const char *vertexSource, - const char *fragmentSource) { - GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource); - if (vertexShader == 0) { - return false; - } - - GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSource); - if (fragmentShader == 0) { - glDeleteShader(vertexShader); - return false; - } - - if (programID_ != 0) { - glDeleteProgram(programID_); - uniformCache_.clear(); - } - - programID_ = glCreateProgram(); - glAttachShader(programID_, vertexShader); - glAttachShader(programID_, fragmentShader); - glLinkProgram(programID_); - - GLint success; - glGetProgramiv(programID_, GL_LINK_STATUS, &success); - if (!success) { - char infoLog[512]; - glGetProgramInfoLog(programID_, 512, nullptr, infoLog); - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Shader program linking failed: %s", infoLog); - glDeleteProgram(programID_); - programID_ = 0; - } - - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); - - return success == GL_TRUE; -} - -/** - * @brief 从二进制数据创建Shader - * @param binary 二进制数据 - * @return 创建成功返回true,失败返回false - */ -bool GLShader::compileFromBinary(const std::vector &binary) { - if (binary.empty()) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Binary data is empty"); - return false; - } - - GLint numFormats = 0; - glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numFormats); - if (numFormats == 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Program binary formats not supported"); - return false; - } - - if (programID_ != 0) { - glDeleteProgram(programID_); - uniformCache_.clear(); - } - - programID_ = glCreateProgram(); - - GLenum binaryFormat = 0; - glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, - reinterpret_cast(&binaryFormat)); - - glProgramBinary(programID_, binaryFormat, binary.data(), - static_cast(binary.size())); - - GLint success = 0; - glGetProgramiv(programID_, GL_LINK_STATUS, &success); - if (!success) { - char infoLog[512]; - glGetProgramInfoLog(programID_, 512, nullptr, infoLog); - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Failed to load shader from binary: %s", infoLog); - glDeleteProgram(programID_); - programID_ = 0; - return false; - } - - return true; -} - -/** - * @brief 获取Shader二进制数据 - * @param outBinary 输出的二进制数据 - * @return 成功返回true,失败返回false - */ -bool GLShader::getBinary(std::vector &outBinary) { - if (programID_ == 0) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Cannot get binary: shader program is 0"); - return false; - } - - GLint binaryLength = 0; - glGetProgramiv(programID_, GL_PROGRAM_BINARY_LENGTH, &binaryLength); - - if (binaryLength <= 0) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Shader binary length is 0 or negative"); - return false; - } - - outBinary.resize(binaryLength); - - GLenum binaryFormat = 0; - GLsizei actualLength = 0; - glGetProgramBinary(programID_, binaryLength, &actualLength, &binaryFormat, - outBinary.data()); - - GLenum err = glGetError(); - if (err != GL_NO_ERROR) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "glGetProgramBinary failed with error: %d", err); - outBinary.clear(); - return false; - } - - if (actualLength == 0) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "glGetProgramBinary returned 0 bytes"); - outBinary.clear(); - return false; - } - - if (actualLength != binaryLength) { - outBinary.resize(actualLength); - } - - return true; -} - -/** - * @brief 编译单个着色器 - * @param type 着色器类型 - * @param source 着色器源码 - * @return 着色器ID,失败返回0 - */ -GLuint GLShader::compileShader(GLenum type, const char *source) { - GLuint shader = glCreateShader(type); - glShaderSource(shader, 1, &source, nullptr); - glCompileShader(shader); - - GLint success; - glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - if (!success) { - char infoLog[512]; - glGetShaderInfoLog(shader, 512, nullptr, infoLog); - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shader compilation failed: %s", - infoLog); - glDeleteShader(shader); - return 0; - } - - 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()) { - return it->second; - } - - GLint location = glGetUniformLocation(programID_, name.c_str()); - uniformCache_[name] = location; - return location; -} - -// ============================================================================ -// GLShaderFactory 实现 -// ============================================================================ - -/** - * @brief 从源码创建Shader - * @param name Shader名称 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 创建的Shader实例 - */ -Ptr GLShaderFactory::createFromSource(const std::string &name, - const std::string &vertSource, - const std::string &fragSource) { - - auto shader = std::make_shared(); - shader->setName(name); - - if (!shader->compileFromSource(vertSource.c_str(), fragSource.c_str())) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Failed to compile shader from source: %s", name.c_str()); - return nullptr; - } - - return shader; -} - -/** - * @brief 从缓存二进制创建Shader - * @param name Shader名称 - * @param binary 编译后的二进制数据 - * @return 创建的Shader实例 - */ -Ptr -GLShaderFactory::createFromBinary(const std::string &name, - const std::vector &binary) { - - auto shader = std::make_shared(); - shader->setName(name); - - if (!shader->compileFromBinary(binary)) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Failed to create shader from binary: %s", name.c_str()); - return nullptr; - } - - return shader; -} - -/** - * @brief 获取Shader的二进制数据 - * @param shader Shader实例 - * @param outBinary 输出的二进制数据 - * @return 成功返回true,失败返回false - */ -bool GLShaderFactory::getShaderBinary(const IShader &shader, - std::vector &outBinary) { - const GLShader *glShader = dynamic_cast(&shader); - if (!glShader) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Shader is not a GLShader instance"); - return false; - } - - return const_cast(glShader)->getBinary(outBinary); -} - -} // namespace frostbite2D diff --git a/Fostbite2D/src/fostbite2D/render/opengl/gl_sprite_batch.cpp b/Fostbite2D/src/fostbite2D/render/opengl/gl_sprite_batch.cpp deleted file mode 100644 index 7a3c8dd..0000000 --- a/Fostbite2D/src/fostbite2D/render/opengl/gl_sprite_batch.cpp +++ /dev/null @@ -1,382 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace frostbite2D { - -// ============================================================================ -// 三角函数查表 - 避免每帧重复计算 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)]; - } - -private: - struct Tables { - std::array sinTable; - std::array cosTable; - - Tables() { - for (size_t i = 0; i < TABLE_SIZE; ++i) { - float angle = - static_cast(i) / INDEX_SCALE * 3.14159265359f / 180.0f; - sinTable[i] = std::sin(angle); - cosTable[i] = std::cos(angle); - } - } - }; - - static size_t normalizeIndexRad(float radians) { - int idx = - static_cast(radians * RAD_TO_INDEX) % static_cast(TABLE_SIZE); - if (idx < 0) { - idx += static_cast(TABLE_SIZE); - } - return static_cast(idx); - } - - static const Tables table_; -}; - -const TrigLookup::Tables TrigLookup::table_; - -// 静态索引生成函数 -/** - * @brief 获取静态索引数组,用于精灵绘制 - * @return 索引数组的常量引用 - */ -static const std::array &getIndices() { - static std::array indices = []() { - std::array arr{}; - for (size_t i = 0; i < GLSpriteBatch::MAX_SPRITES; ++i) { - GLuint base = static_cast(i * GLSpriteBatch::VERTICES_PER_SPRITE); - arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 0] = base + 0; - arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 1] = base + 1; - arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 2] = base + 2; - arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 3] = base + 0; - arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 4] = base + 2; - arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 5] = base + 3; - } - return arr; - }(); - return indices; -} - -/** - * @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() { - // 使用ShaderManager从文件加载着色器 - shader_ = ShaderManager::getInstance().loadFromFile( - "sprite", "shaders/sprite.vert", "shaders/sprite.frag"); - - if (!shader_) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Failed to load sprite batch shader from ShaderManager"); - return false; - } - - // 生成 VAO、VBO、IBO - glGenVertexArrays(1, &vao_); - glGenBuffers(1, &vbo_); - glGenBuffers(1, &ibo_); - - glBindVertexArray(vao_); - - // 设置 VBO - 使用动态绘制模式 - glBindBuffer(GL_ARRAY_BUFFER, vbo_); - glBufferData(GL_ARRAY_BUFFER, MAX_VERTICES * sizeof(Vertex), nullptr, - GL_DYNAMIC_DRAW); - - // 设置顶点属性 - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), - (void *)offsetof(Vertex, position)); - - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), - (void *)offsetof(Vertex, texCoord)); - - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), - (void *)offsetof(Vertex, color)); - - // 使用编译期生成的静态索引缓冲区 - const auto &indices = getIndices(); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), - indices.data(), GL_STATIC_DRAW); - - glBindVertexArray(0); - - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "GLSpriteBatch initialized with capacity for %d sprites", - MAX_SPRITES); - return true; -} - -/** - * @brief 关闭精灵批处理器,释放OpenGL资源 - */ -void GLSpriteBatch::shutdown() { - if (vao_ != 0) { - glDeleteVertexArrays(1, &vao_); - vao_ = 0; - } - if (vbo_ != 0) { - glDeleteBuffers(1, &vbo_); - vbo_ = 0; - } - if (ibo_ != 0) { - glDeleteBuffers(1, &ibo_); - ibo_ = 0; - } - shader_.reset(); -} - -/** - * @brief 开始批处理,设置视图投影矩阵 - * @param viewProjection 视图投影矩阵 - */ -void GLSpriteBatch::begin(const glm::mat4 &viewProjection) { - viewProjection_ = viewProjection; - viewProjectionDirty_ = true; - vertexCount_ = 0; - currentTexture_ = nullptr; - currentIsSDF_ = false; - drawCallCount_ = 0; - spriteCount_ = 0; - batchCount_ = 0; -} - -/** - * @brief 绘制单个精灵,自动处理批处理 - * @param texture 纹理 - * @param data 精灵数据 - */ -void GLSpriteBatch::draw(const Texture &texture, const SpriteData &data) { - // 检查是否需要刷新批次 - if (needsFlush(texture, data.isSDF)) { - flush(); - } - - // 设置当前纹理(如果是第一次绘制或刷新后) - if (currentTexture_ == nullptr) { - currentTexture_ = &texture; - currentIsSDF_ = data.isSDF; - } - - // 添加顶点到缓冲区 - addVertices(data); - vertexCount_ += VERTICES_PER_SPRITE; - ++spriteCount_; -} - -/** - * @brief 批量绘制多个精灵,优化性能 - * @param texture 纹理 - * @param sprites 精灵数据数组 - */ -void GLSpriteBatch::drawBatch(const Texture &texture, - const std::vector &sprites) { - for (const auto &sprite : sprites) { - draw(texture, sprite); - } -} - -/** - * @brief 立即绘制单个精灵,不缓存 - * @param texture 纹理 - * @param data 精灵数据 - */ -void GLSpriteBatch::drawImmediate(const Texture &texture, - const SpriteData &data) { - flush(); // 先刷新当前批次 - - currentTexture_ = &texture; - currentIsSDF_ = data.isSDF; - - addVertices(data); - vertexCount_ = VERTICES_PER_SPRITE; - ++spriteCount_; - - flush(); // 立即刷新 -} - -/** - * @brief 结束批处理,刷新剩余顶点 - */ -void GLSpriteBatch::end() { flush(); } - -/** - * @brief 检查是否需要刷新批次 - * @param texture 纹理 - * @param isSDF 是否SDF字体 - * @return 需要刷新返回true - */ -bool GLSpriteBatch::needsFlush(const Texture &texture, bool isSDF) const { - if (currentTexture_ == nullptr) - return false; - if (currentTexture_ != &texture) - return true; - if (currentIsSDF_ != isSDF) - return true; - if (vertexCount_ + VERTICES_PER_SPRITE > MAX_VERTICES) - return true; - return false; -} - -/** - * @brief 刷新批次,提交顶点数据到GPU - */ -void GLSpriteBatch::flush() { - if (vertexCount_ == 0 || currentTexture_ == nullptr) - return; - - // 绑定纹理 - 将 Texture 转换为 GLTexture - static_cast(currentTexture_)->bind(0); - - // 设置Shader - setupShader(); - - // 更新VBO数据 - glBindBuffer(GL_ARRAY_BUFFER, vbo_); - glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount_ * sizeof(Vertex), - vertexBuffer_.data()); - - // 绘制 - glBindVertexArray(vao_); - GLsizei indexCount = static_cast( - (vertexCount_ / VERTICES_PER_SPRITE) * INDICES_PER_SPRITE); - glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, nullptr); - - ++drawCallCount_; - ++batchCount_; - - // 重置顶点计数 - vertexCount_ = 0; -} - -/** - * @brief 设置Shader uniform变量 - */ -void GLSpriteBatch::setupShader() { - shader_->bind(); - - // 只在矩阵变化时更新 - if (viewProjectionDirty_ || viewProjection_ != cachedViewProjection_) { - shader_->setMat4("uViewProjection", viewProjection_); - cachedViewProjection_ = viewProjection_; - viewProjectionDirty_ = false; - } - - // 设置纹理单元 - shader_->setInt("uTexture", 0); - - shader_->setInt("uUseSDF", currentIsSDF_ ? 1 : 0); -} - -/** - * @brief 添加精灵顶点到缓冲区 - * @param data 精灵数据 - */ -void GLSpriteBatch::addVertices(const SpriteData &data) { - size_t baseIndex = vertexCount_; - - // 计算旋转后的四个角 - float cosRot = TrigLookup::cosRad(data.rotation); - float sinRot = TrigLookup::sinRad(data.rotation); - - // 计算锚点偏移(与 Extra2D-dev 一致) - float anchorOffsetX = data.size.x * data.anchor.x; - float anchorOffsetY = data.size.y * data.anchor.y; - - // 本地坐标(相对于锚点) - // rx0, ry0: 左下 - // rx1, ry1: 右上 - float rx0 = -anchorOffsetX; - float ry0 = -anchorOffsetY; - float rx1 = data.size.x - anchorOffsetX; - float ry1 = data.size.y - anchorOffsetY; - - // 预计算旋转后的偏移 - float cosRx0 = rx0 * cosRot, sinRx0 = rx0 * sinRot; - float cosRx1 = rx1 * cosRot, sinRx1 = rx1 * sinRot; - float cosRy0 = ry0 * cosRot, sinRy0 = ry0 * sinRot; - float cosRy1 = ry1 * cosRot, sinRy1 = ry1 * sinRot; - - // 顶点布局(与 Extra2D-dev 完全一致): - // v3(左上) -- v2(右上) - // | | - // v0(左下) -- v1(右下) - // - // 索引: (0,1,2) 和 (0,2,3) - - glm::vec4 color(data.color.r, data.color.g, data.color.b, data.color.a); - - // v0: 左下 (u0, v0) - vertexBuffer_[baseIndex + 0] = { - glm::vec2(data.position.x + cosRx0 - sinRy0, - data.position.y + sinRx0 + cosRy0), - glm::vec2(data.texCoordMin.x, data.texCoordMin.y), color}; - - // v1: 右下 (u1, v0) - vertexBuffer_[baseIndex + 1] = { - glm::vec2(data.position.x + cosRx1 - sinRy0, - data.position.y + sinRx1 + cosRy0), - glm::vec2(data.texCoordMax.x, data.texCoordMin.y), color}; - - // v2: 右上 (u1, v1) - vertexBuffer_[baseIndex + 2] = { - glm::vec2(data.position.x + cosRx1 - sinRy1, - data.position.y + sinRx1 + cosRy1), - glm::vec2(data.texCoordMax.x, data.texCoordMax.y), color}; - - // v3: 左上 (u0, v1) - vertexBuffer_[baseIndex + 3] = { - glm::vec2(data.position.x + cosRx0 - sinRy1, - data.position.y + sinRx0 + cosRy1), - glm::vec2(data.texCoordMin.x, data.texCoordMax.y), color}; -} - -} // namespace frostbite2D diff --git a/Fostbite2D/src/fostbite2D/render/opengl/gl_texture.cpp b/Fostbite2D/src/fostbite2D/render/opengl/gl_texture.cpp deleted file mode 100644 index 56a6c47..0000000 --- a/Fostbite2D/src/fostbite2D/render/opengl/gl_texture.cpp +++ /dev/null @@ -1,513 +0,0 @@ -#include -#include -#define STB_IMAGE_IMPLEMENTATION -#include -#include -#include - -namespace frostbite2D { - -// ============================================================================ -// KTX 文件头结构 -// ============================================================================ -#pragma pack(push, 1) -struct KTXHeader { - uint8_t identifier[12]; - uint32_t endianness; - uint32_t glType; - uint32_t glTypeSize; - uint32_t glFormat; - uint32_t glInternalFormat; - uint32_t glBaseInternalFormat; - uint32_t pixelWidth; - uint32_t pixelHeight; - uint32_t pixelDepth; - uint32_t numberOfArrayElements; - uint32_t numberOfFaces; - uint32_t numberOfMipmapLevels; - uint32_t bytesOfKeyValueData; -}; -#pragma pack(pop) - -// KTX 文件标识符 -static const uint8_t KTX_IDENTIFIER[12] = {0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, - 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A}; - -// ============================================================================ -// DDS 文件头结构 -// ============================================================================ -#pragma pack(push, 1) -struct DDSPixelFormat { - uint32_t size; - uint32_t flags; - uint32_t fourCC; - uint32_t rgbBitCount; - uint32_t rBitMask; - uint32_t gBitMask; - uint32_t bBitMask; - uint32_t aBitMask; -}; - -struct DDSHeader { - uint32_t magic; - uint32_t size; - uint32_t flags; - uint32_t height; - uint32_t width; - uint32_t pitchOrLinearSize; - uint32_t depth; - uint32_t mipMapCount; - uint32_t reserved1[11]; - DDSPixelFormat pixelFormat; - uint32_t caps; - uint32_t caps2; - uint32_t caps3; - uint32_t caps4; - uint32_t reserved2; -}; - -struct DDSHeaderDXT10 { - uint32_t dxgiFormat; - uint32_t resourceDimension; - uint32_t miscFlag; - uint32_t arraySize; - uint32_t miscFlags2; -}; -#pragma pack(pop) - -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(a) | (static_cast(b) << 8) | - (static_cast(c) << 16) | (static_cast(d) << 24); -} - -// ============================================================================ -// 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) { - // 保存像素数据用于生成遮罩 - if (pixels) { - pixelData_.resize(width * height * channels); - std::memcpy(pixelData_.data(), pixels, pixelData_.size()); - } - 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) { - // 检查是否为压缩纹理格式 - std::string ext = filepath.substr(filepath.find_last_of('.') + 1); - if (ext == "ktx" || ext == "KTX") { - loadCompressed(filepath); - return; - } - if (ext == "dds" || ext == "DDS") { - loadCompressed(filepath); - return; - } - - // 不翻转图片,保持原始方向 - stbi_set_flip_vertically_on_load(false); - uint8_t *data = stbi_load(filepath.c_str(), &width_, &height_, &channels_, 0); - if (data) { - // 保存像素数据用于生成遮罩 - pixelData_.resize(width_ * height_ * channels_); - std::memcpy(pixelData_.data(), data, pixelData_.size()); - - createTexture(data); - stbi_image_free(data); - } else { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load texture: %s", - filepath.c_str()); - } -} - -GLTexture::~GLTexture() { - if (textureID_ != 0) { - glDeleteTextures(1, &textureID_); - textureID_ = 0; - } -} - -void GLTexture::setFilter(bool linear) { - bind(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - linear ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, - linear ? GL_LINEAR : GL_NEAREST); -} - -void GLTexture::setWrap(bool repeat) { - bind(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, - repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, - repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); -} - -void GLTexture::bind(unsigned int slot) const { - glActiveTexture(GL_TEXTURE0 + slot); - 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; - int unpackAlignment = 4; - if (channels_ == 1) { - format = GL_RED; - internalFormat = GL_R8; - unpackAlignment = 1; - format_ = PixelFormat::R8; - } else if (channels_ == 3) { - format = GL_RGB; - internalFormat = GL_RGB8; - unpackAlignment = 1; - format_ = PixelFormat::RGB8; - } else if (channels_ == 4) { - format = GL_RGBA; - internalFormat = GL_RGBA8; - unpackAlignment = 4; - format_ = PixelFormat::RGBA8; - } - - glGenTextures(1, &textureID_); - bind(); - - GLint prevUnpackAlignment = 4; - glGetIntegerv(GL_UNPACK_ALIGNMENT, &prevUnpackAlignment); - glPixelStorei(GL_UNPACK_ALIGNMENT, unpackAlignment); - - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width_, height_, 0, format, - GL_UNSIGNED_BYTE, pixels); - glPixelStorei(GL_UNPACK_ALIGNMENT, prevUnpackAlignment); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - // 使用 NEAREST 过滤器,更适合像素艺术风格的精灵 - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glGenerateMipmap(GL_TEXTURE_2D); - - // 计算数据大小 - dataSize_ = static_cast(width_ * height_ * channels_); -} - -// ============================================================================ -// 压缩纹理加载 -// ============================================================================ - -bool GLTexture::loadCompressed(const std::string &filepath) { - std::string ext = filepath.substr(filepath.find_last_of('.') + 1); - if (ext == "ktx" || ext == "KTX") { - return loadKTX(filepath); - } - if (ext == "dds" || ext == "DDS") { - return loadDDS(filepath); - } - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Unsupported compressed texture format: %s", filepath.c_str()); - 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()) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open KTX file: %s", - filepath.c_str()); - return false; - } - - KTXHeader header; - file.read(reinterpret_cast(&header), sizeof(header)); - if (!file) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to read KTX header: %s", - filepath.c_str()); - return false; - } - - // 验证标识符 - if (std::memcmp(header.identifier, KTX_IDENTIFIER, 12) != 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid KTX identifier: %s", - filepath.c_str()); - return false; - } - - width_ = static_cast(header.pixelWidth); - height_ = static_cast(header.pixelHeight); - channels_ = 4; // 压缩纹理通常解压为 RGBA - - // 确定压缩格式 - GLenum glInternalFormat = header.glInternalFormat; - switch (glInternalFormat) { - case GL_COMPRESSED_RGB8_ETC2: - format_ = PixelFormat::ETC2_RGB8; - channels_ = 3; - break; - case GL_COMPRESSED_RGBA8_ETC2_EAC: - format_ = PixelFormat::ETC2_RGBA8; - break; - case GL_COMPRESSED_RGBA_ASTC_4x4: - format_ = PixelFormat::ASTC_4x4; - break; - case GL_COMPRESSED_RGBA_ASTC_6x6: - format_ = PixelFormat::ASTC_6x6; - break; - case GL_COMPRESSED_RGBA_ASTC_8x8: - format_ = PixelFormat::ASTC_8x8; - break; - default: - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Unsupported KTX internal format: %x", glInternalFormat); - return false; - } - - // 跳过 key-value 数据 - file.seekg(header.bytesOfKeyValueData, std::ios::cur); - - // 读取第一个 mipmap level - uint32_t imageSize = 0; - file.read(reinterpret_cast(&imageSize), sizeof(imageSize)); - if (!file || imageSize == 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Failed to read KTX image size: %s", filepath.c_str()); - return false; - } - - std::vector compressedData(imageSize); - file.read(reinterpret_cast(compressedData.data()), imageSize); - if (!file) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Failed to read KTX image data: %s", filepath.c_str()); - return false; - } - - // 创建 GL 纹理 - glGenTextures(1, &textureID_); - bind(); - - glCompressedTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, width_, height_, 0, - static_cast(imageSize), - compressedData.data()); - - GLenum err = glGetError(); - if (err != GL_NO_ERROR) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "glCompressedTexImage2D failed for KTX: %x", err); - glDeleteTextures(1, &textureID_); - textureID_ = 0; - return false; - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // 数据大小 - dataSize_ = imageSize; - - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Loaded compressed KTX texture: %s (%dx%d, format=%x)", - filepath.c_str(), 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()) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open DDS file: %s", - filepath.c_str()); - return false; - } - - DDSHeader header; - file.read(reinterpret_cast(&header), sizeof(header)); - if (!file) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to read DDS header: %s", - filepath.c_str()); - return false; - } - - if (header.magic != DDS_MAGIC) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid DDS magic: %s", - filepath.c_str()); - return false; - } - - width_ = static_cast(header.width); - height_ = static_cast(header.height); - channels_ = 4; - - GLenum glInternalFormat = 0; - - // 检查 DX10 扩展头 - if ((header.pixelFormat.flags & DDPF_FOURCC) && - header.pixelFormat.fourCC == makeFourCC('D', 'X', '1', '0')) { - DDSHeaderDXT10 dx10Header; - file.read(reinterpret_cast(&dx10Header), sizeof(dx10Header)); - if (!file) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Failed to read DDS DX10 header: %s", filepath.c_str()); - return false; - } - - // DXGI_FORMAT 映射到 GL 格式 - switch (dx10Header.dxgiFormat) { - case 147: // DXGI_FORMAT_ETC2_RGB8 - glInternalFormat = GL_COMPRESSED_RGB8_ETC2; - format_ = PixelFormat::ETC2_RGB8; - channels_ = 3; - break; - case 148: // DXGI_FORMAT_ETC2_RGBA8 - glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; - format_ = PixelFormat::ETC2_RGBA8; - break; - default: - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Unsupported DDS DX10 format: %d", dx10Header.dxgiFormat); - return false; - } - } else { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "DDS file does not use DX10 extension, unsupported: %s", - filepath.c_str()); - return false; - } - - // 计算压缩数据大小 - size_t blockSize = (glInternalFormat == GL_COMPRESSED_RGB8_ETC2) ? 8 : 16; - size_t blocksWide = (width_ + 3) / 4; - size_t blocksHigh = (height_ + 3) / 4; - size_t imageSize = blocksWide * blocksHigh * blockSize; - - std::vector compressedData(imageSize); - file.read(reinterpret_cast(compressedData.data()), imageSize); - if (!file) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Failed to read DDS image data: %s", filepath.c_str()); - return false; - } - - // 创建 GL 纹理 - glGenTextures(1, &textureID_); - bind(); - - glCompressedTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, width_, height_, 0, - static_cast(imageSize), - compressedData.data()); - - GLenum err = glGetError(); - if (err != GL_NO_ERROR) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "glCompressedTexImage2D failed for DDS: %x", err); - glDeleteTextures(1, &textureID_); - textureID_ = 0; - return false; - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // 数据大小 - dataSize_ = imageSize; - - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Loaded compressed DDS texture: %s (%dx%d)", filepath.c_str(), - width_, height_); - return true; -} - -void GLTexture::generateAlphaMask() { - if (pixelData_.empty() || width_ <= 0 || height_ <= 0) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Cannot generate alpha mask: no pixel data available"); - return; - } - - alphaMask_ = std::make_unique(AlphaMask::createFromPixels( - pixelData_.data(), width_, height_, channels_)); - - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, - "Generated alpha mask for texture: %dx%d", width_, height_); -} - -PixelFormat GLTexture::getFormat() const { return format_; } - -/** - * @brief 静态工厂方法,创建指定格式的空纹理 - * @param width 纹理宽度 - * @param height 纹理高度 - * @param format 像素格式 - * @return 创建的纹理智能指针 - */ -Ptr GLTexture::create(int width, int height, PixelFormat format) { - int channels = 4; - switch (format) { - case PixelFormat::R8: - channels = 1; - break; - case PixelFormat::RG8: - channels = 2; - break; - case PixelFormat::RGB8: - channels = 3; - break; - case PixelFormat::RGBA8: - channels = 4; - break; - default: - channels = 4; - break; - } - return makePtr(width, height, nullptr, channels); -} - -} // namespace frostbite2D diff --git a/Fostbite2D/src/fostbite2D/render/shader/shader_manager.cpp b/Fostbite2D/src/fostbite2D/render/shader/shader_manager.cpp deleted file mode 100644 index 6a7bf2c..0000000 --- a/Fostbite2D/src/fostbite2D/render/shader/shader_manager.cpp +++ /dev/null @@ -1,231 +0,0 @@ -#include -#include -#include -#include - -namespace frostbite2D { - -/** - * @brief 从文件读取文本内容 - * @param filepath 文件路径 - * @return 文件内容字符串,失败返回空字符串 - */ -static std::string readFile(const std::filesystem::path &filepath) { - if (!std::filesystem::exists(filepath)) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shader file not found: %s", - filepath.string().c_str()); - return ""; - } - - std::ifstream file(filepath); - if (!file.is_open()) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open shader file: %s", - filepath.string().c_str()); - return ""; - } - - std::stringstream buffer; - buffer << file.rdbuf(); - return buffer.str(); -} - -/** - * @brief 获取单例实例 - * @return Shader管理器实例引用 - */ -ShaderManager &ShaderManager::getInstance() { - static ShaderManager instance; - return instance; -} - -/** - * @brief 初始化Shader系统 - * @param factory 渲染后端Shader工厂 - * @return 初始化成功返回true,失败返回false - */ -bool ShaderManager::init(Ptr factory) { - if (initialized_) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "ShaderManager already initialized"); - return true; - } - - if (!factory) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shader factory is null"); - return false; - } - - factory_ = factory; - initialized_ = true; - - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "ShaderManager initialized"); - return true; -} - -/** - * @brief 关闭Shader系统 - */ -void ShaderManager::shutdown() { - if (!initialized_) { - return; - } - - shaders_.clear(); - factory_.reset(); - initialized_ = false; - - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "ShaderManager shutdown"); -} - -/** - * @brief 从源码加载Shader - * @param name Shader名称 - * @param vertSource 顶点着色器源码 - * @param fragSource 片段着色器源码 - * @return 加载的Shader实例 - */ -Ptr ShaderManager::loadFromSource(const std::string &name, - const std::string &vertSource, - const std::string &fragSource) { - if (!initialized_) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ShaderManager not initialized"); - return nullptr; - } - - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.shader; - } - - Ptr shader = - factory_->createFromSource(name, vertSource, fragSource); - if (!shader) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Failed to create shader from source: %s", name.c_str()); - return nullptr; - } - - ShaderInfo info; - info.shader = shader; - info.vertSource = vertSource; - info.fragSource = fragSource; - - shaders_[name] = std::move(info); - - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Shader loaded from source: %s", - name.c_str()); - return shader; -} - -/** - * @brief 从文件加载Shader - * @param name Shader名称 - * @param vertPath 顶点着色器文件路径 - * @param fragPath 片段着色器文件路径 - * @return 加载的Shader实例,失败返回nullptr - */ -Ptr ShaderManager::loadFromFile(const std::string &name, - const std::filesystem::path &vertPath, - const std::filesystem::path &fragPath) { - if (!initialized_) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ShaderManager not initialized"); - return nullptr; - } - - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.shader; - } - - std::string vertSource = readFile(vertPath); - std::string fragSource = readFile(fragPath); - - if (vertSource.empty()) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Failed to read vertex shader file: %s", vertPath.string().c_str()); - return nullptr; - } - - if (fragSource.empty()) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Failed to read fragment shader file: %s", fragPath.string().c_str()); - return nullptr; - } - - Ptr shader = factory_->createFromSource(name, vertSource, fragSource); - if (!shader) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Failed to create shader from files: %s", name.c_str()); - return nullptr; - } - - ShaderInfo info; - info.shader = shader; - info.vertSource = vertSource; - info.fragSource = fragSource; - info.vertPath = vertPath; - info.fragPath = fragPath; - - shaders_[name] = std::move(info); - - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Shader loaded from file: %s", name.c_str()); - return shader; -} - -/** - * @brief 获取已加载的Shader - * @param name Shader名称 - * @return Shader实例,不存在返回nullptr - */ -Ptr ShaderManager::get(const std::string &name) const { - auto it = shaders_.find(name); - if (it != shaders_.end()) { - return it->second.shader; - } - return nullptr; -} - -/** - * @brief 检查Shader是否存在 - * @param name Shader名称 - * @return 存在返回true,否则返回false - */ -bool ShaderManager::has(const std::string &name) const { - return shaders_.find(name) != shaders_.end(); -} - -/** - * @brief 移除Shader - * @param name Shader名称 - */ -void ShaderManager::remove(const std::string &name) { - auto it = shaders_.find(name); - if (it != shaders_.end()) { - shaders_.erase(it); - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Shader removed: %s", - name.c_str()); - } -} - -/** - * @brief 清除所有Shader - */ -void ShaderManager::clear() { - shaders_.clear(); - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "All shaders cleared"); -} - -/** - * @brief 注册重载回调 - * @param name Shader名称 - * @param callback 重载回调函数 - */ -void ShaderManager::setReloadCallback(const std::string &name, - ShaderReloadCallback callback) { - auto it = shaders_.find(name); - if (it != shaders_.end()) { - it->second.reloadCallback = callback; - } -} - -} // namespace frostbite2D diff --git a/Fostbite2D/src/fostbite2D/render/texture.cpp b/Fostbite2D/src/fostbite2D/render/texture.cpp deleted file mode 100644 index c07b344..0000000 --- a/Fostbite2D/src/fostbite2D/render/texture.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include - -namespace frostbite2D { - -/** - * @brief 从像素数据创建Alpha遮罩 - * @param pixels 像素数据指针 - * @param width 宽度 - * @param height 高度 - * @param channels 通道数 - * @return 创建的AlphaMask - */ -AlphaMask AlphaMask::createFromPixels(const uint8_t *pixels, int width, - int height, int channels) { - AlphaMask mask; - mask.width_ = width; - mask.height_ = height; - mask.data_.resize(width * height); - - for (int y = 0; y < height; ++y) { - for (int x = 0; x < width; ++x) { - int pixelIndex = (y * width + x) * channels; - uint8_t alpha; - - if (channels == 4) { - // RGBA 格式 - alpha = pixels[pixelIndex + 3]; - } else if (channels == 1) { - // 灰度格式 - alpha = pixels[pixelIndex]; - } else { - // RGB 或其他格式,假设不透明 - alpha = 255; - } - - mask.data_[y * width + x] = alpha; - } - } - - return mask; -} - -/** - * @brief 获取指定位置的透明度 - * @param x X坐标 - * @param y Y坐标 - * @return 透明度值(0-255) - */ -uint8_t AlphaMask::getAlpha(int x, int y) const { - if (x < 0 || x >= width_ || y < 0 || y >= height_) { - return 0; - } - return data_[y * width_ + x]; -} - -/** - * @brief 检查指定位置是否不透明 - * @param x X坐标 - * @param y Y坐标 - * @return 不透明返回true - */ -bool AlphaMask::isOpaque(int x, int y) const { - return getAlpha(x, y) > 128; // 使用128作为阈值 -} - -} // namespace frostbite2D diff --git a/Fostbite2D/src/main.cpp b/Fostbite2D/src/main.cpp index 1bc4086..4749f84 100644 --- a/Fostbite2D/src/main.cpp +++ b/Fostbite2D/src/main.cpp @@ -1,13 +1,8 @@ +#include "SDL_log.h" #include -#include -#include +#include #include #include -#include -#include -#include -#include -#include #include #include @@ -24,213 +19,13 @@ int main(int argc, char **argv) { Application &app = Application::get(); if (!app.init(config)) { - std::cerr << "Failed to initialize application!" << std::endl; + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "Failed to initialize application!"); return -1; } - // 初始化 ShaderManager - auto shaderFactory = makePtr(); - if (!ShaderManager::getInstance().init(shaderFactory)) { - std::cerr << "Failed to initialize ShaderManager!" << std::endl; - return -1; - } - - // 创建 OpenGL 渲染器 - GLRenderer renderer; - SDL_Window *sdlWindow = SDL_GL_GetCurrentWindow(); - if (!sdlWindow) { - std::cerr << "Failed to get SDL window!" << std::endl; - return -1; - } - - if (!renderer.init(sdlWindow)) { - std::cerr << "Failed to initialize OpenGL renderer!" << std::endl; - return -1; - } - - // 加载字体(使用系统默认字体或项目字体) -#ifdef _WIN32 - std::string fontPath = "C:/Windows/Fonts/arial.ttf"; // Windows 系统字体 -#else - std::string fontPath = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"; // Linux 系统字体 -#endif - FontAtlas *font = nullptr; - - // 尝试加载字体 - try { - font = new GLFontAtlas(fontPath, 24, false); // 24像素大小,不使用SDF - SDL_Log("Font loaded successfully: %s", fontPath.c_str()); - } catch (...) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to load font: %s", - fontPath.c_str()); - SDL_Log("Text rendering will be disabled"); - } - - // 创建相机 - 视口范围 (0,0) 到 (800,600),相机位置在 (0,0) 表示左上角 - Camera camera(0.0f, 800.0f, 600.0f, 0.0f); - // 相机默认位置 (0,0),表示看向世界坐标的左上角 - - SDL_Log("Frostbite2D OpenGL Renderer initialized successfully!"); - SDL_Log("Press ESC to exit"); - SDL_Log("Use WASD to move camera, Q/E to zoom, R to reset"); - - // 主循环 - bool running = true; - SDL_Event event; - float time = 0.0f; - float zoom = 1.0f; - - while (running) { - // 处理事件 - while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) { - running = false; - } - if (event.type == SDL_KEYDOWN) { - if (event.key.keysym.sym == SDLK_ESCAPE) { - running = false; - } - // 相机控制 - if (event.key.keysym.sym == SDLK_w) { - camera.move( - 0.0f, - 10.0f); // 向上移动(Y轴向下,所以正方向是向下,反方向是向上) - } - if (event.key.keysym.sym == SDLK_s) { - camera.move(0.0f, -10.0f); // 向下移动 - } - if (event.key.keysym.sym == SDLK_a) { - camera.move(10.0f, 0.0f); // 向左移动 - } - if (event.key.keysym.sym == SDLK_d) { - camera.move(-10.0f, 0.0f); // 向右移动 - } - if (event.key.keysym.sym == SDLK_q) { - zoom = std::max(0.5f, zoom - 0.1f); - camera.setZoom(zoom); - } - if (event.key.keysym.sym == SDLK_e) { - zoom = std::min(3.0f, zoom + 0.1f); - camera.setZoom(zoom); - } - if (event.key.keysym.sym == SDLK_r) { - camera.setPosition(0.0f, 0.0f); - zoom = 1.0f; - camera.setZoom(zoom); - } - } - } - - // 更新时间 - time += 0.016f; // 假设 60 FPS - - // 开始渲染帧 - renderer.beginFrame(Color(0.1f, 0.1f, 0.15f, 1.0f)); // 深蓝灰色背景 - - // 设置视口 - renderer.setViewport(0, 0, 800, 600); - - // 使用相机的视图投影矩阵 - renderer.setViewProjection(camera.getViewProjectionMatrix()); - - // 绘制测试图形(使用世界坐标) - - // 1. 绘制红色矩形边框 - renderer.drawRect(Rect(100.0f, 100.0f, 200.0f, 150.0f), Colors::Red, 2.0f); - - // 2. 绘制绿色填充矩形 - renderer.fillRect(Rect(350.0f, 100.0f, 200.0f, 150.0f), Colors::Green); - - // 3. 绘制蓝色圆形 - renderer.fillCircle(Vec2(650.0f, 175.0f), 75.0f, Colors::Blue, 32); - - // 4. 绘制动态旋转的矩形(使用填充三角形组合) - float centerX = 400.0f; - float centerY = 400.0f; - float size = 100.0f; - float angle = time * 2.0f; // 旋转角度 - - // 计算旋转后的四个角 - float cosA = cosf(angle); - float sinA = sinf(angle); - - Vec2 p1(centerX + (-size * cosA - (-size) * sinA), - centerY + (-size * sinA + (-size) * cosA)); - Vec2 p2(centerX + (size * cosA - (-size) * sinA), - centerY + (size * sinA + (-size) * cosA)); - Vec2 p3(centerX + (size * cosA - size * sinA), - centerY + (size * sinA + size * cosA)); - Vec2 p4(centerX + (-size * cosA - size * sinA), - centerY + (-size * sinA + size * cosA)); - - // 绘制旋转的四边形(分成两个三角形) - renderer.fillTriangle(p1, p2, p3, Colors::Yellow); - renderer.fillTriangle(p1, p3, p4, Colors::Yellow); - - // 5. 绘制线条 - renderer.drawLine(Vec2(50.0f, 550.0f), Vec2(750.0f, 550.0f), Colors::White, - 3.0f); - - // 6. 绘制三角形 - renderer.fillTriangle(Vec2(200.0f, 300.0f), Vec2(300.0f, 300.0f), - Vec2(250.0f, 200.0f), Colors::Cyan); - - // 7. 绘制网格(帮助观察相机移动) - for (int i = 0; i <= 800; i += 100) { - renderer.drawLine(Vec2((float)i, 0.0f), Vec2((float)i, 600.0f), - Color(0.2f, 0.2f, 0.2f, 0.5f), 1.0f); - } - for (int i = 0; i <= 600; i += 100) { - renderer.drawLine(Vec2(0.0f, (float)i), Vec2(800.0f, (float)i), - Color(0.2f, 0.2f, 0.2f, 0.5f), 1.0f); - } - - // 8. 绘制文本(使用 GLRenderer 的 drawText) - if (font) { - renderer.beginSpriteBatch(); - - // 测试:直接绘制字体纹理的一部分(第一个字符 'F') - Texture *fontTex = font->getTexture(); - if (fontTex) { - // 绘制字体纹理的一部分来测试 - Rect destRect(50.0f, 300.0f, 64.0f, 64.0f); // 目标位置和大小 - Rect srcRect(0.0f, 0.0f, 64.0f, 64.0f); // 纹理的前64x64像素 - renderer.drawSprite(*fontTex, destRect, srcRect, Colors::White, 0.0f, - Vec2(0, 0)); - SDL_Log("Drawing font texture test at (50, 300)"); - } - - // 绘制标题 - renderer.drawText(*font, "Frostbite2D Engine", 50.0f, 50.0f, - Colors::White); - - // 绘制说明文字 - renderer.drawText(*font, "WASD: Move Camera", 50.0f, 100.0f, - Colors::Yellow); - renderer.drawText(*font, "Q/E: Zoom", 50.0f, 130.0f, Colors::Yellow); - renderer.drawText(*font, "R: Reset", 50.0f, 160.0f, Colors::Yellow); - renderer.drawText(*font, "ESC: Exit", 50.0f, 190.0f, Colors::Yellow); - - // 绘制 FPS 信息 - renderer.drawText(*font, "OpenGL Renderer Active", 50.0f, 250.0f, - Colors::Green); - - renderer.endSpriteBatch(); - } - - // 结束渲染帧 - renderer.endFrame(); - - // 交换缓冲区 - SDL_GL_SwapWindow(sdlWindow); - } - - // 清理资源 - delete font; - renderer.shutdown(); - ShaderManager::getInstance().shutdown(); app.shutdown(); - std::cout << "程序正常退出" << std::endl; + SDL_Log("程序正常退出"); return 0; } diff --git a/platform/switch.lua b/platform/switch.lua new file mode 100644 index 0000000..c1e7d3a --- /dev/null +++ b/platform/switch.lua @@ -0,0 +1,65 @@ + +target("Frostbite2D") + set_kind("binary") + add_files(path.join(os.projectdir(), "Fostbite2D/src/**.cpp")) + add_files(path.join(os.projectdir(), "Fostbite2D/src/**.c")) + add_includedirs(path.join(os.projectdir(), "Fostbite2D/include")) + + -- 检查 DEVKITPRO 环境变量(Windows 上使用 C:/devkitPro) + local devkitPro = os.getenv("DEVKITPRO") or "L:/Switch/devkitPro" + local devkitA64 = path.join(devkitPro, "devkitA64") + + -- 设置工具链路径 + set_toolset("cc", path.join(devkitA64, "bin/aarch64-none-elf-gcc.exe")) + set_toolset("cxx", path.join(devkitA64, "bin/aarch64-none-elf-g++.exe")) + set_toolset("ld", path.join(devkitA64, "bin/aarch64-none-elf-g++.exe")) + set_toolset("ar", path.join(devkitA64, "bin/aarch64-none-elf-gcc-ar.exe")) + set_toolset("strip", path.join(devkitA64, "bin/aarch64-none-elf-strip.exe")) + + -- 架构标志 + local arch_flags = "-march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE" + add_cxflags(arch_flags) + -- 使用 devkitPro 提供的 switch.specs 文件 + add_ldflags("-specs=" .. path.join(devkitPro, "libnx/switch.specs"), "-g", arch_flags) + + -- 定义 Switch 平台宏 + add_defines("__SWITCH__", "__NX__", "MA_SWITCH", "PFD_SWITCH") + + -- SimpleIni 配置:不使用 Windows API + add_defines("SI_NO_CONVERSION") + + -- libnx 路径 - 必须在工具链级别添加 + add_includedirs(path.join(devkitPro, "libnx/include")) + add_linkdirs(path.join(devkitPro, "libnx/lib")) + + -- portlibs 路径(EGL + 桌面 OpenGL + SDL2) + add_includedirs(path.join(devkitPro, "portlibs/switch/include")) + add_includedirs(path.join(devkitPro, "portlibs/switch/include/SDL2")) + add_linkdirs(path.join(devkitPro, "portlibs/switch/lib")) + + add_syslinks("SDL2_mixer", "SDL2", "opusfile", "opus", "vorbisidec", "ogg", + "modplug", "mpg123", "FLAC", "GLESv2", "EGL", "glapi", "drm_nouveau", + {public = true}) + add_syslinks("nx", "m") + + -- 构建后生成 NRO 文件 + after_build(function (target) + local elf_file = target:targetfile() + local output_dir = path.directory(elf_file) + local nacp_file = path.join(output_dir, "hello_world.nacp") + local nro_file = path.join(output_dir, "hello_world.nro") + local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe") + local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.exe") + + if os.isfile(nacptool) and os.isfile(elf2nro) then + os.vrunv(nacptool, {"--create", "Hello World", "Extra2D Team", "1.0.0", nacp_file}) + local romfs = path.join(example_dir, "romfs") + if os.isdir(romfs) then + os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs}) + else + os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file}) + end + print("Generated NRO: " .. nro_file) + end + end) +target_end() diff --git a/shaders/shape.frag b/shaders/shape.frag deleted file mode 100644 index 9c02cad..0000000 --- a/shaders/shape.frag +++ /dev/null @@ -1,8 +0,0 @@ -#version 300 es -precision highp float; -in vec4 vColor; -out vec4 fragColor; - -void main() { - fragColor = vColor; -} diff --git a/shaders/shape.vert b/shaders/shape.vert deleted file mode 100644 index e1f5e9a..0000000 --- a/shaders/shape.vert +++ /dev/null @@ -1,13 +0,0 @@ -#version 300 es -precision highp float; -layout(location = 0) in vec2 aPosition; -layout(location = 1) in vec4 aColor; - -uniform mat4 u_viewProjection; - -out vec4 vColor; - -void main() { - gl_Position = u_viewProjection * vec4(aPosition, 0.0, 1.0); - vColor = aColor; -} diff --git a/shaders/sprite.frag b/shaders/sprite.frag deleted file mode 100644 index faaed13..0000000 --- a/shaders/sprite.frag +++ /dev/null @@ -1,21 +0,0 @@ -#version 300 es -precision highp float; -in vec2 vTexCoord; -in vec4 vColor; - -uniform sampler2D uTexture; -uniform int uUseSDF; - -out vec4 fragColor; - -void main() { - if (uUseSDF == 1) { - float dist = texture(uTexture, vTexCoord).a; - float sd = (dist - 0.502) * 3.98; - float w = fwidth(sd); - float alpha = smoothstep(-w, w, sd); - fragColor = vec4(vColor.rgb, vColor.a * alpha); - } else { - fragColor = texture(uTexture, vTexCoord) * vColor; - } -} diff --git a/shaders/sprite.vert b/shaders/sprite.vert deleted file mode 100644 index 13e6b6e..0000000 --- a/shaders/sprite.vert +++ /dev/null @@ -1,16 +0,0 @@ -#version 300 es -precision highp float; -layout(location = 0) in vec2 aPosition; -layout(location = 1) in vec2 aTexCoord; -layout(location = 2) in vec4 aColor; - -uniform mat4 uViewProjection; - -out vec2 vTexCoord; -out vec4 vColor; - -void main() { - gl_Position = uViewProjection * vec4(aPosition, 0.0, 1.0); - vTexCoord = aTexCoord; - vColor = aColor; -} diff --git a/xmake.lua b/xmake.lua index 70f594c..18cd70c 100644 --- a/xmake.lua +++ b/xmake.lua @@ -14,12 +14,13 @@ local supported_plats = {mingw = true, windows = true, linux = true, macosx = tr -- 自动选择平台 if not supported_plats[target_plat] then - raise("Unsupported platform: " .. target_plat .. ". Supported platforms: mingw, windows, linux, macosx, switch") + os.exists(1) end -- 引入对应平台的配置文件 local platform_config_file = "platform/" .. target_plat .. ".lua" + if os.isfile(platform_config_file) then includes(platform_config_file) else