diff --git a/Extra2D/include/extra2d/extra2d.h b/Extra2D/include/extra2d/extra2d.h index 937b28c..d3f7835 100644 --- a/Extra2D/include/extra2d/extra2d.h +++ b/Extra2D/include/extra2d/extra2d.h @@ -13,8 +13,6 @@ #include #include -// Config removed - app info now in Application class - // Platform #include #include @@ -23,17 +21,26 @@ #include #include -// Graphics -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +// Render Core +#include +#include +#include +#include +#include +#include +#include + +// Render Camera +#include +#include + +// Render Batch +#include +#include +#include + +// Render Renderer +#include // RHI (Render Hardware Interface) #include diff --git a/Extra2D/include/extra2d/render/backends/opengl/gl_context.h b/Extra2D/include/extra2d/render/backends/opengl/gl_context.h new file mode 100644 index 0000000..a14c61a --- /dev/null +++ b/Extra2D/include/extra2d/render/backends/opengl/gl_context.h @@ -0,0 +1,144 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief OpenGL 版本信息 + */ +struct GLVersion { + int major = 0; + int minor = 0; +}; + +/** + * @brief OpenGL 上下文管理类 + * + * 单例模式,管理 OpenGL 上下文初始化和状态查询 + */ +class GLContext { +public: + /** + * @brief 获取单例实例 + */ + static GLContext& get(); + + /** + * @brief 初始化 OpenGL 上下文 + * @return 成功返回 true + */ + bool init(); + + /** + * @brief 关闭 OpenGL 上下文 + */ + void shutdown(); + + /** + * @brief 检查是否已初始化 + */ + bool isInitialized() const { return initialized_; } + + // ======================================================================== + // 版本信息 + // ======================================================================== + + /** + * @brief 获取 OpenGL 版本 + */ + GLVersion getVersion() const { return version_; } + + /** + * @brief 获取版本字符串 + */ + std::string getVersionString() const; + + /** + * @brief 获取渲染器名称 + */ + const std::string& getRenderer() const { return renderer_; } + + /** + * @brief 获取供应商名称 + */ + const std::string& getVendor() const { return vendor_; } + + // ======================================================================== + // 能力查询 + // ======================================================================== + + /** + * @brief 获取最大纹理尺寸 + */ + int getMaxTextureSize() const { return maxTextureSize_; } + + /** + * @brief 获取最大纹理单元数 + */ + int getMaxTextureUnits() const { return maxTextureUnits_; } + + /** + * @brief 获取最大顶点属性数 + */ + int getMaxVertexAttribs() const { return maxVertexAttribs_; } + + /** + * @brief 获取最大 Uniform 缓冲区绑定数 + */ + int getMaxUniformBufferBindings() const { return maxUniformBufferBindings_; } + + /** + * @brief 检查是否支持扩展 + */ + bool hasExtension(const std::string& extension) const; + + // ======================================================================== + // 状态管理 + // ======================================================================== + + /** + * @brief 检查 OpenGL 错误 + */ + bool checkError(const char* operation = nullptr); + + /** + * @brief 清除错误状态 + */ + void clearErrors(); + +private: + GLContext() = default; + ~GLContext() = default; + GLContext(const GLContext&) = delete; + GLContext& operator=(const GLContext&) = delete; + + /** + * @brief 查询 OpenGL 能力 + */ + void queryCapabilities(); + + /** + * @brief 解析版本字符串 + */ + void parseVersion(); + + bool initialized_ = false; + + GLVersion version_; + std::string renderer_; + std::string vendor_; + std::string glslVersion_; + + int maxTextureSize_ = 0; + int maxTextureUnits_ = 0; + int maxVertexAttribs_ = 0; + int maxUniformBufferBindings_ = 0; + + std::unordered_set extensions_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/backends/opengl/gl_render_backend.h b/Extra2D/include/extra2d/render/backends/opengl/gl_render_backend.h new file mode 100644 index 0000000..70edfc8 --- /dev/null +++ b/Extra2D/include/extra2d/render/backends/opengl/gl_render_backend.h @@ -0,0 +1,302 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +class IWindow; + +/** + * @brief OpenGL 渲染后端实现 + * + * 实现 RenderBackend 接口,使用 OpenGL 作为底层图形 API + */ +class GLRenderBackend : public RenderBackend { +public: + /** + * @brief 构造函数 + */ + GLRenderBackend(); + + /** + * @brief 析构函数 + */ + ~GLRenderBackend() override; + + // ======================================================================== + // 生命周期 + // ======================================================================== + + /** + * @brief 初始化渲染后端 + * @param window 窗口接口 + * @param config 配置参数 + * @return 成功返回 true + */ + bool init(IWindow *window, const RenderBackendConfig &config = {}) override; + + /** + * @brief 关闭渲染后端 + */ + void shutdown() override; + + /** + * @brief 检查是否已初始化 + */ + bool isValid() const override; + + // ======================================================================== + // 帧管理 + // ======================================================================== + + /** + * @brief 开始帧渲染 + * @param clearColor 清屏颜色 + */ + void beginFrame(const Color &clearColor = Colors::Black) override; + + /** + * @brief 结束帧渲染 + */ + void endFrame() override; + + /** + * @brief 呈现帧 + */ + void present() override; + + // ======================================================================== + // 渲染状态 + // ======================================================================== + + /** + * @brief 设置视口 + */ + void setViewport(int x, int y, int width, int height) override; + + /** + * @brief 设置裁剪区域 + */ + void setScissor(int x, int y, int width, int height) override; + + /** + * @brief 启用/禁用裁剪测试 + */ + void setScissorEnabled(bool enabled) override; + + /** + * @brief 设置投影矩阵 + */ + void setProjectionMatrix(const glm::mat4 &matrix) override; + + /** + * @brief 设置视图矩阵 + */ + void setViewMatrix(const glm::mat4 &matrix) override; + + /** + * @brief 设置垂直同步 + */ + void setVSync(bool enabled) override; + + /** + * @brief 获取垂直同步状态 + */ + bool isVSyncEnabled() const override; + + // ======================================================================== + // 精灵渲染 + // ======================================================================== + + /** + * @brief 绘制精灵 + */ + void drawSprite(Ptr texture, const Rect &destRect, + const Rect &srcRect = Rect(0, 0, 1, 1), + const Color &color = Colors::White, + float rotation = 0.0f, bool flipX = false, + bool flipY = false) override; + + /** + * @brief 绘制精灵(使用变换矩阵) + */ + void drawSprite(Ptr texture, + const glm::mat4 &transform, + const Rect &srcRect = Rect(0, 0, 1, 1), + const Color &color = Colors::White) override; + + /** + * @brief 绘制九宫格精灵 + */ + void draw9Slice(Ptr texture, const Rect &destRect, + const Rect &srcRect, const Vec2 &borderSize, + const Color &color = Colors::White) override; + + // ======================================================================== + // 文本渲染 + // ======================================================================== + + /** + * @brief 绘制文本 + */ + void drawText(const std::string &text, const Vec2 &position, + float fontSize, const Color &color = Colors::White) override; + + /** + * @brief 绘制文本(UTF-32) + */ + void drawText(const std::u32string &text, const Vec2 &position, + float fontSize, const Color &color = Colors::White) override; + + /** + * @brief 测量文本尺寸 + */ + Vec2 measureText(const std::string &text, float fontSize) override; + + /** + * @brief 测量文本尺寸(UTF-32) + */ + Vec2 measureText(const std::u32string &text, float fontSize) override; + + // ======================================================================== + // 形状渲染 + // ======================================================================== + + /** + * @brief 绘制矩形 + */ + void drawRect(const Rect &rect, const Color &color, + float lineWidth = 1.0f) override; + + /** + * @brief 绘制填充矩形 + */ + void fillRect(const Rect &rect, const Color &color) override; + + /** + * @brief 绘制圆形 + */ + void drawCircle(const Vec2 ¢er, float radius, const Color &color, + float lineWidth = 1.0f, uint32_t segments = 32) override; + + /** + * @brief 绘制填充圆形 + */ + void fillCircle(const Vec2 ¢er, float radius, const Color &color, + uint32_t segments = 32) override; + + /** + * @brief 绘制线段 + */ + void drawLine(const Vec2 &start, const Vec2 &end, const Color &color, + float lineWidth = 1.0f) override; + + /** + * @brief 绘制多边形 + */ + void drawPolygon(const Vec2 *points, size_t count, const Color &color, + float lineWidth = 1.0f) override; + + /** + * @brief 绘制填充多边形 + */ + void fillPolygon(const Vec2 *points, size_t count, + const Color &color) override; + + // ======================================================================== + // 批处理控制 + // ======================================================================== + + /** + * @brief 开始精灵批处理 + */ + void beginSpriteBatch() override; + + /** + * @brief 结束精灵批处理 + */ + void endSpriteBatch() override; + + /** + * @brief 刷新当前批次 + */ + void flush() override; + + /** + * @brief 设置渲染排序键 + */ + void setSortKey(uint64_t key) override; + + /** + * @brief 设置渲染层 + */ + void setLayer(int layer) override; + + /** + * @brief 设置混合模式 + */ + void setBlendMode(rhi::BlendState blend) override; + + // ======================================================================== + // 访问器 + // ======================================================================== + + /** + * @brief 获取 RHI 设备 + */ + rhi::RHIDevice *getDevice() const override; + + /** + * @brief 获取窗口宽度 + */ + int getWidth() const override; + + /** + * @brief 获取窗口高度 + */ + int getHeight() const override; + + /** + * @brief 获取渲染统计 + */ + const RenderStats &getStats() const override; + + /** + * @brief 重置渲染统计 + */ + void resetStats() override; + + /** + * @brief 获取图形 API 类型 + */ + rhi::GraphicsAPI getAPI() const override; + + /** + * @brief 获取设备能力 + */ + const rhi::DeviceCaps &getCaps() const override; + +private: + bool initialized_ = false; + IWindow *window_ = nullptr; + RenderBackendConfig config_; + RenderStats stats_; + + Ptr device_; + glm::mat4 projectionMatrix_ = glm::mat4(1.0f); + glm::mat4 viewMatrix_ = glm::mat4(1.0f); + + int width_ = 0; + int height_ = 0; + + uint64_t sortKey_ = 0; + int layer_ = 0; + rhi::BlendState blendState_ = rhi::BlendState::alphaBlend(); + bool inBatch_ = false; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_device.h b/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_device.h index 4638b85..a442c56 100644 --- a/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_device.h +++ b/Extra2D/include/extra2d/render/backends/opengl/gl_rhi_device.h @@ -3,9 +3,7 @@ #include #include #include -#include #include -#include namespace extra2d { @@ -15,185 +13,198 @@ namespace rhi { /** * @brief OpenGL RHI 设备实现 - * + * * 使用 OpenGL 4.5 Core Profile + DSA 特性 */ class GLRHIDevice : public RHIDevice { public: - /** - * @brief 构造函数 - */ - GLRHIDevice(); + /** + * @brief 构造函数 + */ + GLRHIDevice(); - /** - * @brief 析构函数 - */ - ~GLRHIDevice() override; + /** + * @brief 析构函数 + */ + ~GLRHIDevice() override; - // ======================================================================== - // 生命周期 - // ======================================================================== + // ======================================================================== + // 生命周期 + // ======================================================================== - bool init(IWindow* window) override; - void shutdown() override; - bool isValid() const override { return initialized_; } - GraphicsAPI getAPI() const override { return GraphicsAPI::OpenGL; } - const DeviceCaps& getCaps() const override { return caps_; } + bool init(IWindow *window) override; + void shutdown() override; + bool isValid() const override { return initialized_; } + GraphicsAPI getAPI() const override { return GraphicsAPI::OpenGL; } + const DeviceCaps &getCaps() const override { return caps_; } - // ======================================================================== - // 帧管理 - // ======================================================================== + // ======================================================================== + // 帧管理 + // ======================================================================== - void beginFrame() override; - void endFrame() override; - void present() override; - void setVSync(bool enabled) override; - bool isVSyncEnabled() const override { return vsyncEnabled_; } - void setDefaultFramebufferSize(uint32_t width, uint32_t height) override; + void beginFrame() override; + void endFrame() override; + void present() override; + void setVSync(bool enabled) override; + bool isVSyncEnabled() const override { return vsyncEnabled_; } + void setDefaultFramebufferSize(uint32_t width, uint32_t height) override; - // ======================================================================== - // 资源创建 - 缓冲区 - // ======================================================================== + // ======================================================================== + // 资源创建 - 缓冲区 + // ======================================================================== - Ptr createBuffer(const BufferDesc& desc) override; - Ptr createVertexBuffer(size_t size, BufferUsage usage = BufferUsage::Static, - const void* data = nullptr) override; - Ptr createIndexBuffer(size_t size, BufferUsage usage = BufferUsage::Static, - const void* data = nullptr) override; - Ptr createUniformBuffer(size_t size, BufferUsage usage = BufferUsage::Dynamic) override; + Ptr createBuffer(const BufferDesc &desc) override; + Ptr + createVertexBuffer(size_t size, BufferUsage usage = BufferUsage::Static, + const void *data = nullptr) override; + Ptr createIndexBuffer(size_t size, + BufferUsage usage = BufferUsage::Static, + const void *data = nullptr) override; + Ptr + createUniformBuffer(size_t size, + BufferUsage usage = BufferUsage::Dynamic) override; - // ======================================================================== - // 资源创建 - 纹理 - // ======================================================================== + // ======================================================================== + // 资源创建 - 纹理 + // ======================================================================== - Ptr createTexture(const TextureDesc& desc) override; - Ptr createTexture2D(uint32_t width, uint32_t height, Format format, - const void* data = nullptr) override; - Ptr createSampler(const SamplerDesc& desc) override; - Ptr createTextureView(Ptr texture, const TextureViewDesc& desc) override; + Ptr createTexture(const TextureDesc &desc) override; + Ptr createTexture2D(uint32_t width, uint32_t height, + Format format, + const void *data = nullptr) override; + Ptr createSampler(const SamplerDesc &desc) override; + Ptr createTextureView(Ptr texture, + const TextureViewDesc &desc) override; - // ======================================================================== - // 资源创建 - 着色器 - // ======================================================================== + // ======================================================================== + // 资源创建 - 着色器 + // ======================================================================== - Ptr createShader(const ShaderDesc& desc) override; - Ptr createShaderFromSource(const std::string& name, - const std::string& vertexSource, - const std::string& fragmentSource) override; - Ptr createShaderFromFile(const std::string& vertexPath, - const std::string& fragmentPath) override; + Ptr createShader(const ShaderDesc &desc) override; + Ptr + createShaderFromSource(const std::string &name, + const std::string &vertexSource, + const std::string &fragmentSource) override; + Ptr createShaderFromFile(const std::string &vertexPath, + const std::string &fragmentPath) override; - // ======================================================================== - // 资源创建 - 管线 - // ======================================================================== + // ======================================================================== + // 资源创建 - 管线 + // ======================================================================== - Ptr createPipeline(const PipelineDesc& desc, Ptr shader) override; - Ptr createPipeline(const PipelineBuilder& builder) override; + Ptr createPipeline(const PipelineDesc &desc, + Ptr shader) override; + Ptr createPipeline(const PipelineBuilder &builder) override; - // ======================================================================== - // 资源创建 - 帧缓冲 - // ======================================================================== + // ======================================================================== + // 资源创建 - 帧缓冲 + // ======================================================================== - Ptr createFramebuffer(const FramebufferDesc& desc) override; - Ptr createFramebuffer(const FramebufferBuilder& builder) override; + Ptr createFramebuffer(const FramebufferDesc &desc) override; + Ptr + createFramebuffer(const FramebufferBuilder &builder) override; - // ======================================================================== - // 渲染状态设置 - // ======================================================================== + // ======================================================================== + // 渲染状态设置 + // ======================================================================== - void setViewport(const Viewport& viewport) override; - void setScissorRect(const ScissorRect& rect) override; - void setScissorEnabled(bool enabled) override; + void setViewport(const Viewport &viewport) override; + void setScissorRect(const ScissorRect &rect) override; + void setScissorEnabled(bool enabled) override; - // ======================================================================== - // 渲染命令 - 绑定资源 - // ======================================================================== + // ======================================================================== + // 渲染命令 - 绑定资源 + // ======================================================================== - void setPipeline(Ptr pipeline) override; - void setShader(Ptr shader) override; - void setVertexBuffer(uint32_t slot, Ptr buffer, uint32_t offset = 0) override; - void setIndexBuffer(Ptr buffer, IndexFormat format) override; - void setTexture(uint32_t slot, Ptr texture, Ptr sampler = nullptr) override; - void setUniformBuffer(uint32_t slot, Ptr buffer) override; - void setFramebuffer(Ptr framebuffer) override; - void setDefaultFramebuffer() override; + void setPipeline(Ptr pipeline) override; + void setShader(Ptr shader) override; + void setVertexBuffer(uint32_t slot, Ptr buffer, + uint32_t offset = 0) override; + void setIndexBuffer(Ptr buffer, IndexFormat format) override; + void setTexture(uint32_t slot, Ptr texture, + Ptr sampler = nullptr) override; + void setUniformBuffer(uint32_t slot, Ptr buffer) override; + void setFramebuffer(Ptr framebuffer) override; + void setDefaultFramebuffer() override; - // ======================================================================== - // 渲染命令 - Uniform 设置 - // ======================================================================== + // ======================================================================== + // 渲染命令 - Uniform 设置 + // ======================================================================== - void setUniformFloat(const std::string& name, float value) override; - void setUniformVec2(const std::string& name, const glm::vec2& value) override; - void setUniformVec3(const std::string& name, const glm::vec3& value) override; - void setUniformVec4(const std::string& name, const glm::vec4& value) override; - void setUniformMat4(const std::string& name, const glm::mat4& value) override; - void setUniformInt(const std::string& name, int value) override; + void setUniformFloat(const std::string &name, float value) override; + void setUniformVec2(const std::string &name, const glm::vec2 &value) override; + void setUniformVec3(const std::string &name, const glm::vec3 &value) override; + void setUniformVec4(const std::string &name, const glm::vec4 &value) override; + void setUniformMat4(const std::string &name, const glm::mat4 &value) override; + void setUniformInt(const std::string &name, int value) override; - // ======================================================================== - // 渲染命令 - 绘制 - // ======================================================================== + // ======================================================================== + // 渲染命令 - 绘制 + // ======================================================================== - void draw(const DrawCommand& cmd) override; - void drawIndexed(const DrawIndexedCommand& cmd) override; - void drawIndirect(Ptr indirectBuffer, size_t offset) override; - void drawIndexedIndirect(Ptr indirectBuffer, size_t offset) override; + void draw(const DrawCommand &cmd) override; + void drawIndexed(const DrawIndexedCommand &cmd) override; + void drawIndirect(Ptr indirectBuffer, size_t offset) override; + void drawIndexedIndirect(Ptr indirectBuffer, + size_t offset) override; - // ======================================================================== - // 渲染命令 - 清除 - // ======================================================================== + // ======================================================================== + // 渲染命令 - 清除 + // ======================================================================== - void clearColor(const ColorValue& color) override; - void clearDepth(float depth = 1.0f) override; - void clearStencil(uint8_t stencil = 0) override; - void clear(const ColorValue& color, float depth = 1.0f, uint8_t stencil = 0) override; + void clearColor(const ColorValue &color) override; + void clearDepth(float depth = 1.0f) override; + void clearStencil(uint8_t stencil = 0) override; + void clear(const ColorValue &color, float depth = 1.0f, + uint8_t stencil = 0) override; - // ======================================================================== - // 统计与调试 - // ======================================================================== + // ======================================================================== + // 统计与调试 + // ======================================================================== - const DeviceStats& getStats() const override { return stats_; } - void resetStats() override; - bool checkError() override; - const std::string& getDebugName() const override { return debugName_; } - void setDebugName(const std::string& name) override { debugName_ = name; } + const DeviceStats &getStats() const override { return stats_; } + void resetStats() override; + bool checkError() override; + const std::string &getDebugName() const override { return debugName_; } + void setDebugName(const std::string &name) override { debugName_ = name; } private: - bool initialized_ = false; - bool vsyncEnabled_ = true; - IWindow* window_ = nullptr; - DeviceCaps caps_; - DeviceStats stats_; - std::string debugName_; + bool initialized_ = false; + bool vsyncEnabled_ = true; + IWindow *window_ = nullptr; + DeviceCaps caps_; + DeviceStats stats_; + std::string debugName_; - // 当前绑定的资源 - Ptr currentPipeline_; - Ptr currentShader_; - Ptr currentFramebuffer_; - IndexFormat currentIndexFormat_ = IndexFormat::UInt16; + // 当前绑定的资源 + Ptr currentPipeline_; + Ptr currentShader_; + Ptr currentFramebuffer_; + IndexFormat currentIndexFormat_ = IndexFormat::UInt16; - // 默认帧缓冲尺寸 - uint32_t defaultFBWidth_ = 0; - uint32_t defaultFBHeight_ = 0; + // 默认帧缓冲尺寸 + uint32_t defaultFBWidth_ = 0; + uint32_t defaultFBHeight_ = 0; - // 裁剪测试状态 - bool scissorEnabled_ = false; + // 裁剪测试状态 + bool scissorEnabled_ = false; - /** - * @brief 初始化设备能力 - */ - void initCaps(); + /** + * @brief 初始化设备能力 + */ + void initCaps(); - /** - * @brief 设置默认状态 - */ - void setDefaultState(); + /** + * @brief 设置默认状态 + */ + void setDefaultState(); - /** - * @brief 应用管线状态 - */ - void applyPipelineState(const BlendState& blend, - const DepthStencilState& depthStencil, - const RasterizerState& rasterizer); + /** + * @brief 应用管线状态 + */ + void applyPipelineState(const BlendState &blend, + const DepthStencilState &depthStencil, + const RasterizerState &rasterizer); }; } // namespace rhi diff --git a/Extra2D/include/extra2d/render/camera/camera.h b/Extra2D/include/extra2d/render/camera/camera.h new file mode 100644 index 0000000..a4d9485 --- /dev/null +++ b/Extra2D/include/extra2d/render/camera/camera.h @@ -0,0 +1,230 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +class ViewportAdapter; + +/** + * @brief 相机类 + * + * 2D 相机,支持平移、缩放、旋转和边界限制 + */ +class Camera { +public: + Camera(); + Camera(float left, float right, float bottom, float top); + ~Camera() = default; + + // ======================================================================== + // 视口设置 + // ======================================================================== + + /** + * @brief 设置视口 + */ + void setViewport(float left, float right, float bottom, float top); + + /** + * @brief 设置视口尺寸 + */ + void setViewportSize(float width, float height); + + /** + * @brief 获取视口 + */ + Rect getViewport() const; + + /** + * @brief 获取视口宽度 + */ + float getViewportWidth() const { return right_ - left_; } + + /** + * @brief 获取视口高度 + */ + float getViewportHeight() const { return top_ - bottom_; } + + // ======================================================================== + // 位置和变换 + // ======================================================================== + + /** + * @brief 设置位置 + */ + void setPos(const Vec2& position); + void setPos(float x, float y); + + /** + * @brief 获取位置 + */ + Vec2 getPosition() const { return position_; } + + /** + * @brief 移动相机 + */ + void move(const Vec2& offset); + void move(float x, float y); + + /** + * @brief 设置旋转角度(度) + */ + void setRotation(float degrees); + + /** + * @brief 获取旋转角度(度) + */ + float getRotation() const { return rotation_; } + + /** + * @brief 旋转相机 + */ + void rotate(float degrees); + + /** + * @brief 设置缩放 + */ + void setZoom(float zoom); + + /** + * @brief 获取缩放 + */ + float getZoom() const { return zoom_; } + + /** + * @brief 缩放相机 + */ + void zoomBy(float factor); + + // ======================================================================== + // 边界限制 + // ======================================================================== + + /** + * @brief 设置边界 + */ + void setBounds(const Rect& bounds); + + /** + * @brief 清除边界 + */ + void clearBounds(); + + /** + * @brief 获取边界 + */ + Rect getBounds() const { return bounds_; } + + /** + * @brief 是否有边界限制 + */ + bool hasBounds() const { return hasBounds_; } + + // ======================================================================== + // 矩阵 + // ======================================================================== + + /** + * @brief 获取视图矩阵 + */ + glm::mat4 getViewMatrix() const; + + /** + * @brief 获取投影矩阵 + */ + glm::mat4 getProjectionMatrix() const; + + /** + * @brief 获取视图投影矩阵 + */ + glm::mat4 getViewProjectionMatrix() const; + + /** + * @brief 更新矩阵 + */ + void updateMatrices(); + + // ======================================================================== + // 坐标转换 + // ======================================================================== + + /** + * @brief 屏幕坐标转世界坐标 + */ + Vec2 screenToWorld(const Vec2& screenPos) const; + + /** + * @brief 世界坐标转屏幕坐标 + */ + Vec2 worldToScreen(const Vec2& worldPos) const; + + /** + * @brief 注视目标 + */ + void lookAt(const Vec2& target); + + // ======================================================================== + // 视口适配器 + // ======================================================================== + + /** + * @brief 设置视口适配器 + */ + void setViewportAdapter(ViewportAdapter* adapter) { viewportAdapter_ = adapter; } + + /** + * @brief 获取视口适配器 + */ + ViewportAdapter* getViewportAdapter() const { return viewportAdapter_; } + + /** + * @brief 应用视口适配器 + */ + void applyViewportAdapter(); + + // ======================================================================== + // 调试 + // ======================================================================== + + /** + * @brief 获取调试名称 + */ + const std::string& getDebugName() const { return debugName_; } + + /** + * @brief 设置调试名称 + */ + void setDebugName(const std::string& name) { debugName_ = name; } + +private: + /** + * @brief 应用边界限制 + */ + void applyBounds(); + + Vec2 position_; + float rotation_; + float zoom_; + + float left_; + float right_; + float bottom_; + float top_; + + Rect bounds_; + bool hasBounds_; + + glm::mat4 viewMatrix_; + glm::mat4 projectionMatrix_; + glm::mat4 viewProjectionMatrix_; + bool matricesDirty_; + + ViewportAdapter* viewportAdapter_; + std::string debugName_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/camera/viewport_adapter.h b/Extra2D/include/extra2d/render/camera/viewport_adapter.h new file mode 100644 index 0000000..dbd83e4 --- /dev/null +++ b/Extra2D/include/extra2d/render/camera/viewport_adapter.h @@ -0,0 +1,254 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +/** + * @brief 视口缩放模式 + */ +enum class ViewportScaleMode { + None, // 无缩放 + Letterbox, // 保持宽高比,黑边 + Crop, // 保持宽高比,裁剪 + Stretch, // 拉伸填充 + IntegerScale // 整数缩放 +}; + +/** + * @brief 视口对齐方式 + */ +enum class ViewportAlign { + Center, + TopLeft, + TopCenter, + TopRight, + CenterLeft, + CenterRight, + BottomLeft, + BottomCenter, + BottomRight +}; + +/** + * @brief 视口配置 + */ +struct ViewportConfig { + float designWidth = 1280.0f; + float designHeight = 720.0f; + ViewportScaleMode scaleMode = ViewportScaleMode::Letterbox; + ViewportAlign align = ViewportAlign::Center; + bool integerScaleOnly = false; + float minScale = 0.5f; + float maxScale = 4.0f; + + ViewportConfig() = default; + + ViewportConfig(float width, float height, + ViewportScaleMode mode = ViewportScaleMode::Letterbox) + : designWidth(width), designHeight(height), scaleMode(mode) {} + + /** + * @brief 创建 HD (1280x720) 配置 + */ + static ViewportConfig hd() { + return ViewportConfig(1280.0f, 720.0f); + } + + /** + * @brief 创建 Full HD (1920x1080) 配置 + */ + static ViewportConfig fullHd() { + return ViewportConfig(1920.0f, 1080.0f); + } + + /** + * @brief 创建 4K (3840x2160) 配置 + */ + static ViewportConfig uhd4k() { + return ViewportConfig(3840.0f, 2160.0f); + } +}; + +/** + * @brief 视口计算结果 + */ +struct ViewportResult { + float x = 0.0f; + float y = 0.0f; + float width = 0.0f; + float height = 0.0f; + float scale = 1.0f; + int offsetX = 0; + int offsetY = 0; + int screenWidth = 0; + int screenHeight = 0; + + ViewportResult() = default; + + /** + * @brief 获取视口矩形 + */ + Rect getRect() const { return Rect(x, y, width, height); } + + /** + * @brief 获取缩放后的设计尺寸 + */ + Size getScaledDesignSize() const { return Size(width / scale, height / scale); } +}; + +/** + * @brief 视口适配器 + * + * 处理不同屏幕尺寸和宽高比的视口适配 + */ +class ViewportAdapter { +public: + ViewportAdapter(); + explicit ViewportAdapter(const ViewportConfig& config); + ~ViewportAdapter() = default; + + // ======================================================================== + // 配置 + // ======================================================================== + + /** + * @brief 设置配置 + */ + void setConfig(const ViewportConfig& config); + + /** + * @brief 获取配置 + */ + const ViewportConfig& getConfig() const { return config_; } + + /** + * @brief 设置设计尺寸 + */ + void setDesignSize(float width, float height); + + /** + * @brief 设置缩放模式 + */ + void setScaleMode(ViewportScaleMode mode); + + /** + * @brief 设置对齐方式 + */ + void setAlign(ViewportAlign align); + + // ======================================================================== + // 更新 + // ======================================================================== + + /** + * @brief 更新视口 + */ + void update(int screenWidth, int screenHeight); + + /** + * @brief 获取结果 + */ + const ViewportResult& getResult() const { return result_; } + + // ======================================================================== + // 查询 + // ======================================================================== + + /** + * @brief 获取设计宽度 + */ + float getDesignWidth() const { return config_.designWidth; } + + /** + * @brief 获取设计高度 + */ + float getDesignHeight() const { return config_.designHeight; } + + /** + * @brief 获取设计宽高比 + */ + float getDesignAspectRatio() const; + + /** + * @brief 获取屏幕宽度 + */ + int getScreenWidth() const { return result_.screenWidth; } + + /** + * @brief 获取屏幕高度 + */ + int getScreenHeight() const { return result_.screenHeight; } + + /** + * @brief 获取屏幕宽高比 + */ + float getScreenAspectRatio() const; + + /** + * @brief 获取当前缩放 + */ + float getScale() const { return result_.scale; } + + /** + * @brief 获取视口偏移 X + */ + int getOffsetX() const { return result_.offsetX; } + + /** + * @brief 获取视口偏移 Y + */ + int getOffsetY() const { return result_.offsetY; } + + // ======================================================================== + // 坐标转换 + // ======================================================================== + + /** + * @brief 屏幕坐标转设计坐标 + */ + Vec2 screenToDesign(const Vec2& screenPos) const; + + /** + * @brief 设计坐标转屏幕坐标 + */ + Vec2 designToScreen(const Vec2& designPos) const; + + // ======================================================================== + // 调试 + // ======================================================================== + + /** + * @brief 获取调试名称 + */ + const std::string& getDebugName() const { return debugName_; } + + /** + * @brief 设置调试名称 + */ + void setDebugName(const std::string& name) { debugName_ = name; } + +private: + /** + * @brief 计算视口 + */ + void calculateViewport(); + + /** + * @brief 计算缩放 + */ + float calculateScale() const; + + /** + * @brief 计算偏移 + */ + void calculateOffset(); + + ViewportConfig config_; + ViewportResult result_; + std::string debugName_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/core/backend_factory.h b/Extra2D/include/extra2d/render/core/backend_factory.h new file mode 100644 index 0000000..d067bc9 --- /dev/null +++ b/Extra2D/include/extra2d/render/core/backend_factory.h @@ -0,0 +1,182 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 渲染后端工厂 + * + * 管理渲染后端的注册和创建 + * 支持多后端(OpenGL、Vulkan、DirectX 等) + */ +class BackendFactory { +public: + /** + * @brief 后端创建函数类型 + */ + using BackendFn = std::function()>; + + /** + * @brief 后端注册信息 + */ + struct BackendInfo { + std::string name; + BackendFn createFn; + std::vector compatibleWindowBackends; + rhi::GraphicsAPI api; + std::string description; + int priority; + }; + + /** + * @brief 注册渲染后端 + * @param name 后端名称(如 "opengl", "vulkan") + * @param backend 后端创建函数 + * @param windowBackends 兼容的窗口后端列表 + * @param api 图形 API 类型 + * @param description 后端描述 + * @param priority 优先级(用于默认选择) + */ + static void reg(const std::string& name, + BackendFn backend, + const std::vector& windowBackends = {}, + rhi::GraphicsAPI api = rhi::GraphicsAPI::OpenGL, + const std::string& description = "", + int priority = 0); + + /** + * @brief 注销渲染后端 + * @param name 后端名称 + */ + static void unreg(const std::string& name); + + /** + * @brief 创建渲染后端 + * @param name 后端名称 + * @return 后端实例,失败返回 nullptr + */ + static UniquePtr createBackend(const std::string& name); + + /** + * @brief 创建默认渲染后端 + * + * 按优先级选择最佳可用后端 + * @return 后端实例,失败返回 nullptr + */ + static UniquePtr createDefaultBackend(); + + /** + * @brief 根据窗口后端创建兼容的渲染后端 + * @param windowBackend 窗口后端名称 + * @return 后端实例,失败返回 nullptr + */ + static UniquePtr createBackendForWindow(const std::string& windowBackend); + + /** + * @brief 根据图形 API 创建后端 + * @param api 图形 API 类型 + * @return 后端实例,失败返回 nullptr + */ + static UniquePtr createBackendForAPI(rhi::GraphicsAPI api); + + /** + * @brief 检查后端是否已注册 + * @param name 后端名称 + */ + static bool hasBackend(const std::string& name); + + /** + * @brief 获取所有已注册的后端名称 + */ + static std::vector getBackendNames(); + + /** + * @brief 获取后端信息 + * @param name 后端名称 + * @return 后端信息,不存在返回 nullptr + */ + static const BackendInfo* getBackendInfo(const std::string& name); + + /** + * @brief 获取所有后端信息 + */ + static std::vector getAllBackendInfos(); + + /** + * @brief 获取兼容指定窗口后端的所有渲染后端 + * @param windowBackend 窗口后端名称 + */ + static std::vector getCompatibleBackends(const std::string& windowBackend); + + /** + * @brief 清除所有注册的后端 + */ + static void clear(); + + /** + * @brief 获取默认后端名称 + */ + static std::string getDefaultBackendName(); + + /** + * @brief 设置默认后端名称 + */ + static void setDefaultBackendName(const std::string& name); + +private: + static std::unordered_map& getRegistry(); + static std::mutex& getMutex(); + static std::string& getDefaultName(); +}; + +/** + * @brief 后端自动注册器 + * + * 用于静态初始化时自动注册后端 + */ +class BackendRegistrar { +public: + BackendRegistrar(const std::string& name, + BackendFactory::BackendFn backend, + const std::vector& windowBackends = {}, + rhi::GraphicsAPI api = rhi::GraphicsAPI::OpenGL, + const std::string& description = "", + int priority = 0) { + BackendFactory::reg(name, backend, windowBackends, api, description, priority); + } +}; + +/** + * @brief 渲染后端注册宏 + * + * 使用方法: + * E2D_REG_RENDER_BACKEND(opengl, GLRenderBackend, {"sdl2", "glfw"}) + */ +#define E2D_REG_RENDER_BACKEND(name, BackendClass, windowBackends) \ + static ::extra2d::BackendRegistrar E2D_CONCAT(backend_reg_, name)( \ + #name, \ + []() -> ::extra2d::UniquePtr<::extra2d::RenderBackend> { \ + return ::extra2d::makeUnique(); \ + }, \ + windowBackends) + +/** + * @brief 渲染后端注册宏(带优先级) + */ +#define E2D_REG_RENDER_BACKEND_EX(name, BackendClass, windowBackends, api, desc, prio) \ + static ::extra2d::BackendRegistrar E2D_CONCAT(backend_reg_, name)( \ + #name, \ + []() -> ::extra2d::UniquePtr<::extra2d::RenderBackend> { \ + return ::extra2d::makeUnique(); \ + }, \ + windowBackends, api, desc, prio) + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/core/render_backend.h b/Extra2D/include/extra2d/render/core/render_backend.h new file mode 100644 index 0000000..738deff --- /dev/null +++ b/Extra2D/include/extra2d/render/core/render_backend.h @@ -0,0 +1,324 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +class IWindow; + +/** + * @brief 渲染统计信息 + */ +struct RenderStats { + uint32_t drawCalls = 0; + uint32_t spriteCount = 0; + uint32_t textCharCount = 0; + uint32_t shapeCount = 0; + uint32_t batchCount = 0; + uint32_t textureBinds = 0; + uint32_t shaderBinds = 0; + + void reset() { + drawCalls = 0; + spriteCount = 0; + textCharCount = 0; + shapeCount = 0; + batchCount = 0; + textureBinds = 0; + shaderBinds = 0; + } +}; + +/** + * @brief 渲染后端配置 + */ +struct RenderBackendConfig { + rhi::GraphicsAPI api = rhi::GraphicsAPI::OpenGL; + bool vsync = true; + uint32_t maxBatchSize = 10000; + bool enableDebug = false; +}; + +/** + * @brief 高层渲染后端接口 + * + * 封装 RHI 层,提供游戏逻辑使用的简化渲染接口 + * 支持精灵、文本、形状等高层渲染功能 + */ +class RenderBackend { +public: + virtual ~RenderBackend() = default; + + // ======================================================================== + // 生命周期 + // ======================================================================== + + /** + * @brief 初始化渲染后端 + * @param window 窗口接口 + * @param config 配置参数 + * @return 成功返回 true + */ + virtual bool init(IWindow *window, + const RenderBackendConfig &config = {}) = 0; + + /** + * @brief 关闭渲染后端 + */ + virtual void shutdown() = 0; + + /** + * @brief 检查是否已初始化 + */ + virtual bool isValid() const = 0; + + // ======================================================================== + // 帧管理 + // ======================================================================== + + /** + * @brief 开始帧渲染 + * @param clearColor 清屏颜色 + */ + virtual void beginFrame(const Color &clearColor = Colors::Black) = 0; + + /** + * @brief 结束帧渲染 + */ + virtual void endFrame() = 0; + + /** + * @brief 呈现帧 + */ + virtual void present() = 0; + + // ======================================================================== + // 渲染状态 + // ======================================================================== + + /** + * @brief 设置视口 + */ + virtual void setViewport(int x, int y, int width, int height) = 0; + + /** + * @brief 设置裁剪区域 + */ + virtual void setScissor(int x, int y, int width, int height) = 0; + + /** + * @brief 启用/禁用裁剪测试 + */ + virtual void setScissorEnabled(bool enabled) = 0; + + /** + * @brief 设置投影矩阵 + */ + virtual void setProjectionMatrix(const glm::mat4 &matrix) = 0; + + /** + * @brief 设置视图矩阵 + */ + virtual void setViewMatrix(const glm::mat4 &matrix) = 0; + + /** + * @brief 设置垂直同步 + */ + virtual void setVSync(bool enabled) = 0; + + /** + * @brief 获取垂直同步状态 + */ + virtual bool isVSyncEnabled() const = 0; + + // ======================================================================== + // 精灵渲染 + // ======================================================================== + + /** + * @brief 绘制精灵 + * @param texture 纹理 + * @param destRect 目标矩形 + * @param srcRect 源矩形(纹理坐标) + * @param color 颜色调制 + * @param rotation 旋转角度(度) + * @param flipX 水平翻转 + * @param flipY 垂直翻转 + */ + virtual void drawSprite(Ptr texture, const Rect &destRect, + const Rect &srcRect = Rect(0, 0, 1, 1), + const Color &color = Colors::White, + float rotation = 0.0f, bool flipX = false, + bool flipY = false) = 0; + + /** + * @brief 绘制精灵(使用变换矩阵) + */ + virtual void drawSprite(Ptr texture, + const glm::mat4 &transform, + const Rect &srcRect = Rect(0, 0, 1, 1), + const Color &color = Colors::White) = 0; + + /** + * @brief 绘制九宫格精灵 + */ + virtual void draw9Slice(Ptr texture, const Rect &destRect, + const Rect &srcRect, const Vec2 &borderSize, + const Color &color = Colors::White) = 0; + + // ======================================================================== + // 文本渲染 + // ======================================================================== + + /** + * @brief 绘制文本 + * @param text UTF-8 编码文本 + * @param position 位置 + * @param fontSize 字体大小 + * @param color 颜色 + */ + virtual void drawText(const std::string &text, const Vec2 &position, + float fontSize, const Color &color = Colors::White) = 0; + + /** + * @brief 绘制文本(UTF-32) + */ + virtual void drawText(const std::u32string &text, const Vec2 &position, + float fontSize, const Color &color = Colors::White) = 0; + + /** + * @brief 测量文本尺寸 + */ + virtual Vec2 measureText(const std::string &text, float fontSize) = 0; + + /** + * @brief 测量文本尺寸(UTF-32) + */ + virtual Vec2 measureText(const std::u32string &text, float fontSize) = 0; + + // ======================================================================== + // 形状渲染 + // ======================================================================== + + /** + * @brief 绘制矩形 + */ + virtual void drawRect(const Rect &rect, const Color &color, + float lineWidth = 1.0f) = 0; + + /** + * @brief 绘制填充矩形 + */ + virtual void fillRect(const Rect &rect, const Color &color) = 0; + + /** + * @brief 绘制圆形 + */ + virtual void drawCircle(const Vec2 ¢er, float radius, const Color &color, + float lineWidth = 1.0f, uint32_t segments = 32) = 0; + + /** + * @brief 绘制填充圆形 + */ + virtual void fillCircle(const Vec2 ¢er, float radius, const Color &color, + uint32_t segments = 32) = 0; + + /** + * @brief 绘制线段 + */ + virtual void drawLine(const Vec2 &start, const Vec2 &end, const Color &color, + float lineWidth = 1.0f) = 0; + + /** + * @brief 绘制多边形 + */ + virtual void drawPolygon(const Vec2 *points, size_t count, const Color &color, + float lineWidth = 1.0f) = 0; + + /** + * @brief 绘制填充多边形 + */ + virtual void fillPolygon(const Vec2 *points, size_t count, + const Color &color) = 0; + + // ======================================================================== + // 批处理控制 + // ======================================================================== + + /** + * @brief 开始精灵批处理 + */ + virtual void beginSpriteBatch() = 0; + + /** + * @brief 结束精灵批处理 + */ + virtual void endSpriteBatch() = 0; + + /** + * @brief 刷新当前批次 + */ + virtual void flush() = 0; + + /** + * @brief 设置渲染排序键 + */ + virtual void setSortKey(uint64_t key) = 0; + + /** + * @brief 设置渲染层 + */ + virtual void setLayer(int layer) = 0; + + /** + * @brief 设置混合模式 + */ + virtual void setBlendMode(rhi::BlendState blend) = 0; + + // ======================================================================== + // 访问器 + // ======================================================================== + + /** + * @brief 获取 RHI 设备 + */ + virtual rhi::RHIDevice *getDevice() const = 0; + + /** + * @brief 获取窗口宽度 + */ + virtual int getWidth() const = 0; + + /** + * @brief 获取窗口高度 + */ + virtual int getHeight() const = 0; + + /** + * @brief 获取渲染统计 + */ + virtual const RenderStats &getStats() const = 0; + + /** + * @brief 重置渲染统计 + */ + virtual void resetStats() = 0; + + /** + * @brief 获取图形 API 类型 + */ + virtual rhi::GraphicsAPI getAPI() const = 0; + + /** + * @brief 获取设备能力 + */ + virtual const rhi::DeviceCaps &getCaps() const = 0; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/core/render_command.h b/Extra2D/include/extra2d/render/core/render_command.h index 2f62a9b..0dc5961 100644 --- a/Extra2D/include/extra2d/render/core/render_command.h +++ b/Extra2D/include/extra2d/render/core/render_command.h @@ -30,6 +30,7 @@ enum class RenderCommandType : uint8_t { * @brief 形状类型枚举 */ enum class ShapeType : uint8_t { + Point, Line, Rect, FilledRect, diff --git a/Extra2D/include/extra2d/render/core/render_context.h b/Extra2D/include/extra2d/render/core/render_context.h index 69ab206..ef2aa56 100644 --- a/Extra2D/include/extra2d/render/core/render_context.h +++ b/Extra2D/include/extra2d/render/core/render_context.h @@ -3,217 +3,183 @@ #include #include #include +#include #include #include #include #include #include -#include namespace extra2d { class IWindow; -/** - * @brief 渲染统计信息 - */ -struct RenderStats { - uint32_t drawCalls; - uint32_t batchCount; - uint32_t vertexCount; - uint32_t triangleCount; - uint32_t spriteCount; - uint32_t textCount; - uint32_t shapeCount; - float frameTime; - float renderTime; - - RenderStats() - : drawCalls(0), batchCount(0), vertexCount(0), triangleCount(0), - spriteCount(0), textCount(0), shapeCount(0), frameTime(0.0f), renderTime(0.0f) {} - - void reset() { - drawCalls = 0; - batchCount = 0; - vertexCount = 0; - triangleCount = 0; - spriteCount = 0; - textCount = 0; - shapeCount = 0; - frameTime = 0.0f; - renderTime = 0.0f; - } -}; - /** * @brief 渲染上下文配置 */ struct RenderContextConfig { - uint32_t maxBatchSize; - bool enableBatching; - bool enableScissor; - bool enableStats; - rhi::GraphicsAPI preferredAPI; + uint32_t maxBatchSize; + bool enableBatching; + bool enableScissor; + bool enableStats; + rhi::GraphicsAPI preferredAPI; - RenderContextConfig() - : maxBatchSize(10000), - enableBatching(true), - enableScissor(true), - enableStats(true), - preferredAPI(rhi::GraphicsAPI::OpenGL) {} + RenderContextConfig() + : maxBatchSize(10000), enableBatching(true), enableScissor(true), + enableStats(true), preferredAPI(rhi::GraphicsAPI::OpenGL) {} }; /** * @brief 渲染上下文 - * + * * 管理渲染状态、命令队列和 RHI 设备 */ class RenderContext { public: - static constexpr uint32_t DEFAULT_MAX_BATCH_SIZE = 10000; + static constexpr uint32_t DEFAULT_MAX_BATCH_SIZE = 10000; - RenderContext(); - explicit RenderContext(const RenderContextConfig& config); - ~RenderContext(); + RenderContext(); + explicit RenderContext(const RenderContextConfig &config); + ~RenderContext(); - /** - * @brief 初始化渲染上下文 - */ - bool init(IWindow* window); - bool init(IWindow* window, rhi::GraphicsAPI api); + /** + * @brief 初始化渲染上下文 + */ + bool init(IWindow *window); + bool init(IWindow *window, rhi::GraphicsAPI api); - /** - * @brief 关闭渲染上下文 - */ - void shutdown(); + /** + * @brief 关闭渲染上下文 + */ + void shutdown(); - /** - * @brief 检查是否有效 - */ - bool isValid() const; + /** + * @brief 检查是否有效 + */ + bool isValid() const; - /** - * @brief 开始帧 - */ - void beginFrame(); + /** + * @brief 开始帧 + */ + void beginFrame(); - /** - * @brief 结束帧 - */ - void endFrame(); + /** + * @brief 结束帧 + */ + void endFrame(); - /** - * @brief 呈现帧 - */ - void present(); + /** + * @brief 呈现帧 + */ + void present(); - /** - * @brief 清除屏幕 - */ - void clear(const Color& color = Colors::Black); - void clear(const Color& color, float depth, uint8_t stencil = 0); + /** + * @brief 清除屏幕 + */ + void clear(const Color &color = Colors::Black); + void clear(const Color &color, float depth, uint8_t stencil = 0); - /** - * @brief 获取/设置视口 - */ - void setViewport(const rhi::Viewport& viewport); - void setViewport(float x, float y, float width, float height); - rhi::Viewport getViewport() const; + /** + * @brief 获取/设置视口 + */ + void setViewport(const rhi::Viewport &viewport); + void setViewport(float x, float y, float width, float height); + rhi::Viewport getViewport() const; - /** - * @brief 获取/设置裁剪区域 - */ - void setScissorRect(const rhi::ScissorRect& rect); - void setScissorRect(int32_t x, int32_t y, uint32_t width, uint32_t height); - void setScissorEnabled(bool enabled); - rhi::ScissorRect getScissorRect() const; + /** + * @brief 获取/设置裁剪区域 + */ + void setScissorRect(const rhi::ScissorRect &rect); + void setScissorRect(int32_t x, int32_t y, uint32_t width, uint32_t height); + void setScissorEnabled(bool enabled); + rhi::ScissorRect getScissorRect() const; - /** - * @brief 变换矩阵栈操作 - */ - void pushTransform(const glm::mat4& transform); - void popTransform(); - void resetTransform(); - glm::mat4 getCurrentTransform() const; + /** + * @brief 变换矩阵栈操作 + */ + void pushTransform(const glm::mat4 &transform); + void popTransform(); + void resetTransform(); + glm::mat4 getCurrentTransform() const; - /** - * @brief 混合状态操作 - */ - void setBlendState(const rhi::BlendState& blend); - rhi::BlendState getBlendState() const; - void pushBlendState(const rhi::BlendState& blend); - void popBlendState(); + /** + * @brief 混合状态操作 + */ + void setBlendState(const rhi::BlendState &blend); + rhi::BlendState getBlendState() const; + void pushBlendState(const rhi::BlendState &blend); + void popBlendState(); - /** - * @brief 渲染队列操作 - */ - RenderQueue& getQueue() { return queue_; } - const RenderQueue& getQueue() const { return queue_; } - void clearQueue(); + /** + * @brief 渲染队列操作 + */ + RenderQueue &getQueue() { return queue_; } + const RenderQueue &getQueue() const { return queue_; } + void clearQueue(); - /** - * @brief 提交渲染队列 - */ - void flushQueue(); + /** + * @brief 提交渲染队列 + */ + void flushQueue(); - /** - * @brief 获取 RHI 设备 - */ - Ptr getDevice() const { return device_; } + /** + * @brief 获取 RHI 设备 + */ + Ptr getDevice() const { return device_; } - /** - * @brief 获取窗口 - */ - IWindow* getWindow() const { return window_; } + /** + * @brief 获取窗口 + */ + IWindow *getWindow() const { return window_; } - /** - * @brief 获取统计信息 - */ - const RenderStats& getStats() const { return stats_; } - void resetStats(); + /** + * @brief 获取统计信息 + */ + const RenderStats &getStats() const { return stats_; } + void resetStats(); - /** - * @brief 获取配置 - */ - const RenderContextConfig& getConfig() const { return config_; } - void setConfig(const RenderContextConfig& config); + /** + * @brief 获取配置 + */ + const RenderContextConfig &getConfig() const { return config_; } + void setConfig(const RenderContextConfig &config); - /** - * @brief 获取帧缓冲尺寸 - */ - uint32_t getFramebufferWidth() const { return fbWidth_; } - uint32_t getFramebufferHeight() const { return fbHeight_; } + /** + * @brief 获取帧缓冲尺寸 + */ + uint32_t getFramebufferWidth() const { return fbWidth_; } + uint32_t getFramebufferHeight() const { return fbHeight_; } - /** - * @brief 设置垂直同步 - */ - void setVSync(bool enabled); - bool isVSyncEnabled() const; + /** + * @brief 设置垂直同步 + */ + void setVSync(bool enabled); + bool isVSyncEnabled() const; - /** - * @brief 设置默认帧缓冲尺寸 - */ - void setDefaultFramebufferSize(uint32_t width, uint32_t height); + /** + * @brief 设置默认帧缓冲尺寸 + */ + void setDefaultFramebufferSize(uint32_t width, uint32_t height); private: - void initDefaultState(); + void initDefaultState(); - Ptr device_; - IWindow* window_; - RenderQueue queue_; - RenderStats stats_; - RenderContextConfig config_; + Ptr device_; + IWindow *window_; + RenderQueue queue_; + RenderStats stats_; + RenderContextConfig config_; - std::stack transformStack_; - std::stack blendStack_; - rhi::BlendState currentBlend_; - rhi::Viewport viewport_; - rhi::ScissorRect scissorRect_; - bool scissorEnabled_; + std::stack transformStack_; + std::stack blendStack_; + rhi::BlendState currentBlend_; + rhi::Viewport viewport_; + rhi::ScissorRect scissorRect_; + bool scissorEnabled_; - uint32_t fbWidth_; - uint32_t fbHeight_; - bool initialized_; + uint32_t fbWidth_; + uint32_t fbHeight_; + bool initialized_; }; } // namespace extra2d diff --git a/Extra2D/include/extra2d/render/core/render_module.h b/Extra2D/include/extra2d/render/core/render_module.h new file mode 100644 index 0000000..4480c18 --- /dev/null +++ b/Extra2D/include/extra2d/render/core/render_module.h @@ -0,0 +1,145 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +class IWindow; +class Renderer; + +/** + * @brief 渲染模块配置 + */ +struct RenderModuleConfig { + rhi::GraphicsAPI api = rhi::GraphicsAPI::OpenGL; + bool vsync = true; + uint32_t maxBatchSize = 10000; + bool enableDebug = false; + float designWidth = 1280.0f; + float designHeight = 720.0f; + + RenderModuleConfig() = default; + + /** + * @brief 创建 OpenGL 配置 + */ + static RenderModuleConfig opengl() { + RenderModuleConfig config; + config.api = rhi::GraphicsAPI::OpenGL; + return config; + } + + /** + * @brief 创建 HD 配置 (1280x720) + */ + static RenderModuleConfig hd() { + RenderModuleConfig config; + config.designWidth = 1280.0f; + config.designHeight = 720.0f; + return config; + } + + /** + * @brief 创建 Full HD 配置 (1920x1080) + */ + static RenderModuleConfig fullHd() { + RenderModuleConfig config; + config.designWidth = 1920.0f; + config.designHeight = 1080.0f; + return config; + } +}; + +/** + * @brief 渲染模块 + * + * 作为 Module 集成到 Application 中 + * 管理渲染器的生命周期 + */ +class RenderModule : public Module { +public: + RenderModule(); + explicit RenderModule(const RenderModuleConfig& config); + ~RenderModule() override; + + // ======================================================================== + // Module 接口实现 + // ======================================================================== + + /** + * @brief 初始化模块 + */ + bool init() override; + + /** + * @brief 关闭模块 + */ + void shutdown() override; + + /** + * @brief 检查模块是否就绪 + */ + bool ok() const override { return initialized_; } + + /** + * @brief 获取模块名称 + */ + const char* name() const override { return "RenderModule"; } + + /** + * @brief 获取模块优先级 + */ + int priority() const override { return 10; } + + /** + * @brief 获取模块依赖 + */ + std::vector deps() const override; + + // ======================================================================== + // 渲染器访问 + // ======================================================================== + + /** + * @brief 获取渲染器 + */ + class Renderer* getRenderer() const { return renderer_.get(); } + + /** + * @brief 获取渲染器(智能指针) + */ + Ptr getRendererPtr() const { return renderer_; } + + // ======================================================================== + // 配置 + // ======================================================================== + + /** + * @brief 获取配置 + */ + const RenderModuleConfig& getConfig() const { return config_; } + + /** + * @brief 设置配置 + */ + void setConfig(const RenderModuleConfig& config); + + // ======================================================================== + // 静态工厂 + // ======================================================================== + + /** + * @brief 创建渲染模块 + */ + static Ptr create(const RenderModuleConfig& config = {}); + +private: + RenderModuleConfig config_; + Ptr renderer_; + bool initialized_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/render/renderer.h b/Extra2D/include/extra2d/render/renderer.h new file mode 100644 index 0000000..44c8026 --- /dev/null +++ b/Extra2D/include/extra2d/render/renderer.h @@ -0,0 +1,326 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +class IWindow; +class Scene; + +/** + * @brief 渲染器配置 + */ +struct RendererConfig { + rhi::GraphicsAPI api = rhi::GraphicsAPI::OpenGL; + bool vsync = true; + uint32_t maxBatchSize = 10000; + bool enableDebug = false; + ViewportConfig viewportConfig; + + RendererConfig() = default; + + /** + * @brief 创建 OpenGL 配置 + */ + static RendererConfig opengl() { + RendererConfig config; + config.api = rhi::GraphicsAPI::OpenGL; + return config; + } +}; + +/** + * @brief 高层渲染器 + * + * 整合渲染后端、批处理器和相机系统 + * 提供统一的渲染接口供游戏逻辑使用 + */ +class Renderer : public Module { +public: + Renderer(); + explicit Renderer(const RendererConfig &config); + ~Renderer() override; + + // ======================================================================== + // Module 接口实现 + // ======================================================================== + + /** + * @brief 初始化渲染器 + */ + bool init() override; + + /** + * @brief 关闭渲染器 + */ + void shutdown() override; + + /** + * @brief 检查渲染器是否就绪 + */ + bool ok() const override { return initialized_; } + + /** + * @brief 获取模块名称 + */ + const char *name() const override { return "Renderer"; } + + /** + * @brief 获取模块优先级 + */ + int priority() const override { return 10; } + + // ======================================================================== + // 初始化 + // ======================================================================== + + /** + * @brief 使用窗口初始化 + */ + bool init(IWindow *window); + + /** + * @brief 使用窗口和配置初始化 + */ + bool init(IWindow *window, const RendererConfig &config); + + /** + * @brief 检查是否已初始化 + */ + bool isInitialized() const { return initialized_; } + + // ======================================================================== + // 帧管理 + // ======================================================================== + + /** + * @brief 开始帧渲染 + */ + void beginFrame(const Color &clearColor = Colors::Black); + + /** + * @brief 结束帧渲染 + */ + void endFrame(); + + /** + * @brief 呈现帧 + */ + void present(); + + // ======================================================================== + // 场景渲染 + // ======================================================================== + + /** + * @brief 渲染场景 + */ + void renderScene(Scene *scene); + + /** + * @brief 渲染场景(指定相机) + */ + void renderScene(Scene *scene, Camera *camera); + + // ======================================================================== + // 相机系统 + // ======================================================================== + + /** + * @brief 设置活动相机 + */ + void setActiveCamera(Ptr camera); + + /** + * @brief 获取活动相机 + */ + Ptr getActiveCamera() const; + + /** + * @brief 获取默认相机 + */ + Ptr getDefaultCamera() const { return defaultCamera_; } + + /** + * @brief 获取视口适配器 + */ + ViewportAdapter &getViewportAdapter() { return viewportAdapter_; } + + /** + * @brief 获取视口适配器(const) + */ + const ViewportAdapter &getViewportAdapter() const { return viewportAdapter_; } + + // ======================================================================== + // 渲染状态 + // ======================================================================== + + /** + * @brief 设置视口 + */ + void setViewport(int x, int y, int width, int height); + + /** + * @brief 设置裁剪区域 + */ + void setScissor(int x, int y, int width, int height); + + /** + * @brief 启用/禁用裁剪测试 + */ + void setScissorEnabled(bool enabled); + + /** + * @brief 设置垂直同步 + */ + void setVSync(bool enabled); + + /** + * @brief 获取垂直同步状态 + */ + bool isVSyncEnabled() const; + + // ======================================================================== + // 批处理器访问 + // ======================================================================== + + /** + * @brief 获取精灵批处理器 + */ + SpriteBatcher *getSpriteBatcher() const { return spriteBatcher_.get(); } + + /** + * @brief 获取形状批处理器 + */ + ShapeBatcher *getShapeBatcher() const { return shapeBatcher_.get(); } + + /** + * @brief 获取文本批处理器 + */ + TextBatcher *getTextBatcher() const { return textBatcher_.get(); } + + // ======================================================================== + // 渲染上下文 + // ======================================================================== + + /** + * @brief 获取渲染上下文 + */ + RenderContext *getContext() const { return context_.get(); } + + /** + * @brief 获取渲染后端 + */ + RenderBackend *getBackend() const { return backend_.get(); } + + /** + * @brief 获取 RHI 设备 + */ + rhi::RHIDevice *getDevice() const; + + // ======================================================================== + // 统计信息 + // ======================================================================== + + /** + * @brief 获取渲染统计 + */ + const RenderStats &getStats() const; + + /** + * @brief 重置统计 + */ + void resetStats(); + + // ======================================================================== + // 配置 + // ======================================================================== + + /** + * @brief 获取配置 + */ + const RendererConfig &getConfig() const { return config_; } + + /** + * @brief 设置配置 + */ + void setConfig(const RendererConfig &config); + + // ======================================================================== + // 窗口信息 + // ======================================================================== + + /** + * @brief 获取窗口宽度 + */ + int getWidth() const { return width_; } + + /** + * @brief 获取窗口高度 + */ + int getHeight() const { return height_; } + + /** + * @brief 获取窗口 + */ + IWindow *getWindow() const { return window_; } + + // ======================================================================== + // 静态工厂 + // ======================================================================== + + /** + * @brief 创建渲染器 + */ + static Ptr create(const RendererConfig &config = {}); + +private: + /** + * @brief 创建批处理器 + */ + bool createBatchers(); + + /** + * @brief 创建默认相机 + */ + void createDefaultCamera(); + + /** + * @brief 更新视口适配器 + */ + void updateViewportAdapter(); + + /** + * @brief 应用相机变换 + */ + void applyCameraTransform(Camera *camera); + + IWindow *window_; + RendererConfig config_; + + UniquePtr context_; + UniquePtr backend_; + UniquePtr spriteBatcher_; + UniquePtr shapeBatcher_; + UniquePtr textBatcher_; + + ViewportAdapter viewportAdapter_; + Ptr activeCamera_; + Ptr defaultCamera_; + + int width_; + int height_; + bool initialized_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/resources/texture.h b/Extra2D/include/extra2d/resources/texture.h new file mode 100644 index 0000000..c787e52 --- /dev/null +++ b/Extra2D/include/extra2d/resources/texture.h @@ -0,0 +1,186 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 纹理过滤模式 + */ +enum class TextureFilter { + Nearest, + Linear, + NearestMipmapNearest, + LinearMipmapNearest, + NearestMipmapLinear, + LinearMipmapLinear +}; + +/** + * @brief 纹理寻址模式 + */ +enum class TextureWrap { + Repeat, + MirroredRepeat, + ClampToEdge, + ClampToBorder +}; + +/** + * @brief 纹理配置 + */ +struct TextureConfig { + TextureFilter minFilter = TextureFilter::Linear; + TextureFilter magFilter = TextureFilter::Linear; + TextureWrap wrapU = TextureWrap::ClampToEdge; + TextureWrap wrapV = TextureWrap::ClampToEdge; + bool generateMipmaps = true; + bool premultipliedAlpha = false; + + TextureConfig() = default; +}; + +/** + * @brief 纹理类 + * + * 封装 RHI 纹理,提供高层纹理管理功能 + */ +class Texture { +public: + Texture(); + explicit Texture(Ptr rhiTexture); + ~Texture() = default; + + // ======================================================================== + // 工厂方法 + // ======================================================================== + + /** + * @brief 从文件加载纹理 + */ + static Ptr load(const std::string& path, const TextureConfig& config = {}); + + /** + * @brief 从内存创建纹理 + */ + static Ptr create(uint32_t width, uint32_t height, + const void* data = nullptr, + rhi::Format format = rhi::Format::RGBA8_UNORM, + const TextureConfig& config = {}); + + /** + * @brief 创建纯色纹理 + */ + static Ptr createSolidColor(uint32_t width, uint32_t height, const Color& color); + + // ======================================================================== + // 属性 + // ======================================================================== + + /** + * @brief 获取宽度 + */ + uint32_t getWidth() const { return width_; } + + /** + * @brief 获取高度 + */ + uint32_t getHeight() const { return height_; } + + /** + * @brief 获取尺寸 + */ + Size getSize() const { return Size(static_cast(width_), static_cast(height_)); } + + /** + * @brief 获取格式 + */ + rhi::Format getFormat() const { return format_; } + + /** + * @brief 检查是否有效 + */ + bool isValid() const { return rhiTexture_ != nullptr; } + + /** + * @brief 获取 RHI 纹理 + */ + Ptr getRHITexture() const { return rhiTexture_; } + + // ======================================================================== + // 数据操作 + // ======================================================================== + + /** + * @brief 更新纹理数据 + */ + bool updateData(const void* data, uint32_t x = 0, uint32_t y = 0, + uint32_t width = 0, uint32_t height = 0); + + /** + * @brief 获取纹理数据 + */ + std::vector getData() const; + + // ======================================================================== + // 配置 + // ======================================================================== + + /** + * @brief 设置过滤模式 + */ + void setFilter(TextureFilter minFilter, TextureFilter magFilter); + + /** + * @brief 设置寻址模式 + */ + void setWrap(TextureWrap wrapU, TextureWrap wrapV); + + /** + * @brief 生成 Mipmap + */ + void generateMipmaps(); + + // ======================================================================== + // 路径 + // ======================================================================== + + /** + * @brief 获取源文件路径 + */ + const std::string& getPath() const { return path_; } + + /** + * @brief 设置源文件路径 + */ + void setPath(const std::string& path) { path_ = path; } + + // ======================================================================== + // 调试 + // ======================================================================== + + /** + * @brief 获取调试名称 + */ + const std::string& getDebugName() const { return debugName_; } + + /** + * @brief 设置调试名称 + */ + void setDebugName(const std::string& name) { debugName_ = name; } + +private: + Ptr rhiTexture_; + uint32_t width_ = 0; + uint32_t height_ = 0; + rhi::Format format_ = rhi::Format::RGBA8_UNORM; + std::string path_; + std::string debugName_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/node.h b/Extra2D/include/extra2d/scene/node.h index 495798c..0191894 100644 --- a/Extra2D/include/extra2d/scene/node.h +++ b/Extra2D/include/extra2d/scene/node.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include #include @@ -21,598 +21,594 @@ class RenderQueue; /** * @brief 节点基类 - 场景图的基础 - * + * * 节点是场景图的基本单元,支持层级管理、变换继承和组件系统 */ class Node : public std::enable_shared_from_this { public: - /** - * @brief 创建节点 - * @param name 节点名称 - * @return 创建的节点智能指针 - */ - static Ptr create(const std::string& name = ""); + /** + * @brief 创建节点 + * @param name 节点名称 + * @return 创建的节点智能指针 + */ + static Ptr create(const std::string &name = ""); - Node(); - virtual ~Node(); + Node(); + virtual ~Node(); - // ------------------------------------------------------------------------- - // 层级管理 - // ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // 层级管理 + // ------------------------------------------------------------------------- - /** - * @brief 添加子节点 - * @param child 要添加的子节点智能指针 - */ - void addChild(Ptr child); + /** + * @brief 添加子节点 + * @param child 要添加的子节点智能指针 + */ + void addChild(Ptr child); - /** - * @brief 批量添加子节点 - * @param children 子节点列表 - */ - void addChildren(std::vector>&& children); + /** + * @brief 批量添加子节点 + * @param children 子节点列表 + */ + void addChildren(std::vector> &&children); - /** - * @brief 移除子节点 - * @param child 要移除的子节点智能指针 - */ - void removeChild(Ptr child); + /** + * @brief 移除子节点 + * @param child 要移除的子节点智能指针 + */ + void removeChild(Ptr child); - /** - * @brief 通过名称移除子节点 - * @param name 子节点的名称 - */ - void removeChildByName(const std::string& name); + /** + * @brief 通过名称移除子节点 + * @param name 子节点的名称 + */ + void removeChildByName(const std::string &name); - /** - * @brief 从父节点分离 - */ - void detach(); + /** + * @brief 从父节点分离 + */ + void detach(); - /** - * @brief 清除所有子节点 - */ - void clearChildren(); + /** + * @brief 清除所有子节点 + */ + void clearChildren(); - /** - * @brief 获取父节点 - * @return 父节点智能指针 - */ - Ptr getParent() const { return parent_.lock(); } + /** + * @brief 获取父节点 + * @return 父节点智能指针 + */ + Ptr getParent() const { return parent_.lock(); } - /** - * @brief 获取所有子节点 - * @return 子节点列表的常量引用 - */ - const std::vector>& getChildren() const { return children_; } + /** + * @brief 获取所有子节点 + * @return 子节点列表的常量引用 + */ + const std::vector> &getChildren() const { return children_; } - /** - * @brief 通过名称查找子节点 - * @param name 子节点的名称 - * @return 找到的子节点智能指针,未找到返回nullptr - */ - Ptr findChild(const std::string& name) const; + /** + * @brief 通过名称查找子节点 + * @param name 子节点的名称 + * @return 找到的子节点智能指针,未找到返回nullptr + */ + Ptr findChild(const std::string &name) const; - /** - * @brief 通过标签查找子节点 - * @param tag 子节点的标签值 - * @return 找到的子节点智能指针,未找到返回nullptr - */ - Ptr findChildByTag(int tag) const; + /** + * @brief 通过标签查找子节点 + * @param tag 子节点的标签值 + * @return 找到的子节点智能指针,未找到返回nullptr + */ + Ptr findChildByTag(int tag) const; - // ------------------------------------------------------------------------- - // 组件系统 - // ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // 组件系统 + // ------------------------------------------------------------------------- - /** - * @brief 添加组件 - * @tparam T 组件类型 - * @tparam Args 构造函数参数类型 - * @param args 构造函数参数 - * @return 创建的组件指针 - */ - template - T* addComponent(Args&&... args) { - static_assert(std::is_base_of::value, - "T must derive from Component"); - - uint32_t typeId = T::getStaticTypeId(); - - // 检查是否已存在相同类型的组件 - auto it = components_.find(typeId); - if (it != components_.end()) { - return static_cast(it->second.get()); - } + /** + * @brief 添加组件 + * @tparam T 组件类型 + * @tparam Args 构造函数参数类型 + * @param args 构造函数参数 + * @return 创建的组件指针 + */ + template T *addComponent(Args &&...args) { + static_assert(std::is_base_of::value, + "T must derive from Component"); - // 创建新组件 - auto component = makePtr(std::forward(args)...); - component->setNode(this); - component->init(); - component->setInitialized(true); - - components_[typeId] = component; - - return component.get(); + uint32_t typeId = T::getStaticTypeId(); + + // 检查是否已存在相同类型的组件 + auto it = components_.find(typeId); + if (it != components_.end()) { + return static_cast(it->second.get()); } - /** - * @brief 获取组件 - * @tparam T 组件类型 - * @return 组件指针,不存在返回nullptr - */ - template - T* getComponent() const { - static_assert(std::is_base_of::value, - "T must derive from Component"); - - uint32_t typeId = T::getStaticTypeId(); - auto it = components_.find(typeId); - if (it != components_.end()) { - return static_cast(it->second.get()); - } - return nullptr; + // 创建新组件 + auto component = makePtr(std::forward(args)...); + component->setNode(this); + component->init(); + component->setInitialized(true); + + components_[typeId] = component; + + return component.get(); + } + + /** + * @brief 获取组件 + * @tparam T 组件类型 + * @return 组件指针,不存在返回nullptr + */ + template T *getComponent() const { + static_assert(std::is_base_of::value, + "T must derive from Component"); + + uint32_t typeId = T::getStaticTypeId(); + auto it = components_.find(typeId); + if (it != components_.end()) { + return static_cast(it->second.get()); } + return nullptr; + } - /** - * @brief 检查是否有组件 - * @tparam T 组件类型 - * @return 如果存在返回true - */ - template - bool hasComponent() const { - return getComponent() != nullptr; + /** + * @brief 检查是否有组件 + * @tparam T 组件类型 + * @return 如果存在返回true + */ + template bool hasComponent() const { + return getComponent() != nullptr; + } + + /** + * @brief 移除组件 + * @tparam T 组件类型 + */ + template void removeComponent() { + static_assert(std::is_base_of::value, + "T must derive from Component"); + + uint32_t typeId = T::getStaticTypeId(); + auto it = components_.find(typeId); + if (it != components_.end()) { + it->second->destroy(); + it->second->setInitialized(false); + it->second->setNode(nullptr); + components_.erase(it); } + } - /** - * @brief 移除组件 - * @tparam T 组件类型 - */ - template - void removeComponent() { - static_assert(std::is_base_of::value, - "T must derive from Component"); - - uint32_t typeId = T::getStaticTypeId(); - auto it = components_.find(typeId); - if (it != components_.end()) { - it->second->destroy(); - it->second->setInitialized(false); - it->second->setNode(nullptr); - components_.erase(it); - } - } + /** + * @brief 获取变换组件 + * @return 变换组件指针 + */ + TransformComponent *getTransform() const { return transform_; } - /** - * @brief 获取变换组件 - * @return 变换组件指针 - */ - TransformComponent* getTransform() const { return transform_; } + // ------------------------------------------------------------------------- + // 变换属性(便捷方法,委托给TransformComponent) + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------- - // 变换属性(便捷方法,委托给TransformComponent) - // ------------------------------------------------------------------------- + /** + * @brief 设置节点位置 + * @param pos 新的位置坐标 + */ + void setPos(const Vec2 &pos); - /** - * @brief 设置节点位置 - * @param pos 新的位置坐标 - */ - void setPos(const Vec2& pos); + /** + * @brief 设置节点位置 + * @param x X坐标 + * @param y Y坐标 + */ + void setPos(float x, float y); - /** - * @brief 设置节点位置 - * @param x X坐标 - * @param y Y坐标 - */ - void setPos(float x, float y); + /** + * @brief 获取节点位置 + * @return 位置坐标 + */ + Vec2 getPosition() const; - /** - * @brief 获取节点位置 - * @return 位置坐标 - */ - Vec2 getPosition() const; + /** + * @brief 设置节点旋转角度 + * @param degrees 旋转角度(度数) + */ + void setRotation(float degrees); - /** - * @brief 设置节点旋转角度 - * @param degrees 旋转角度(度数) - */ - void setRotation(float degrees); + /** + * @brief 获取节点旋转角度 + * @return 旋转角度(度数) + */ + float getRotation() const; - /** - * @brief 获取节点旋转角度 - * @return 旋转角度(度数) - */ - float getRotation() const; + /** + * @brief 设置节点缩放 + * @param scale 缩放向量 + */ + void setScale(const Vec2 &scale); - /** - * @brief 设置节点缩放 - * @param scale 缩放向量 - */ - void setScale(const Vec2& scale); + /** + * @brief 设置节点统一缩放 + * @param scale 统一缩放值 + */ + void setScale(float scale); - /** - * @brief 设置节点统一缩放 - * @param scale 统一缩放值 - */ - void setScale(float scale); + /** + * @brief 设置节点缩放 + * @param x X轴缩放值 + * @param y Y轴缩放值 + */ + void setScale(float x, float y); - /** - * @brief 设置节点缩放 - * @param x X轴缩放值 - * @param y Y轴缩放值 - */ - void setScale(float x, float y); + /** + * @brief 获取节点缩放 + * @return 缩放向量 + */ + Vec2 getScale() const; - /** - * @brief 获取节点缩放 - * @return 缩放向量 - */ - Vec2 getScale() const; + /** + * @brief 设置节点锚点 + * @param anchor 锚点位置(0-1范围) + */ + void setAnchor(const Vec2 &anchor); - /** - * @brief 设置节点锚点 - * @param anchor 锚点位置(0-1范围) - */ - void setAnchor(const Vec2& anchor); + /** + * @brief 设置节点锚点 + * @param x 锚点X坐标(0-1范围) + * @param y 锚点Y坐标(0-1范围) + */ + void setAnchor(float x, float y); - /** - * @brief 设置节点锚点 - * @param x 锚点X坐标(0-1范围) - * @param y 锚点Y坐标(0-1范围) - */ - void setAnchor(float x, float y); + /** + * @brief 获取节点锚点 + * @return 锚点位置 + */ + Vec2 getAnchor() const; - /** - * @brief 获取节点锚点 - * @return 锚点位置 - */ - Vec2 getAnchor() const; + /** + * @brief 设置节点斜切 + * @param skew 斜切角度向量 + */ + void setSkew(const Vec2 &skew); - /** - * @brief 设置节点斜切 - * @param skew 斜切角度向量 - */ - void setSkew(const Vec2& skew); + /** + * @brief 设置节点斜切 + * @param x X轴斜切角度 + * @param y Y轴斜切角度 + */ + void setSkew(float x, float y); - /** - * @brief 设置节点斜切 - * @param x X轴斜切角度 - * @param y Y轴斜切角度 - */ - void setSkew(float x, float y); + /** + * @brief 获取节点斜切 + * @return 斜切角度向量 + */ + Vec2 getSkew() const; - /** - * @brief 获取节点斜切 - * @return 斜切角度向量 - */ - Vec2 getSkew() const; + // ------------------------------------------------------------------------- + // 世界变换 + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------- - // 世界变换 - // ------------------------------------------------------------------------- + /** + * @brief 将本地坐标转换为世界坐标 + * @param localPos 本地坐标位置 + * @return 世界坐标位置 + */ + Vec2 toWorld(const Vec2 &localPos) const; - /** - * @brief 将本地坐标转换为世界坐标 - * @param localPos 本地坐标位置 - * @return 世界坐标位置 - */ - Vec2 toWorld(const Vec2& localPos) const; + /** + * @brief 将世界坐标转换为本地坐标 + * @param worldPos 世界坐标位置 + * @return 本地坐标位置 + */ + Vec2 toLocal(const Vec2 &worldPos) const; - /** - * @brief 将世界坐标转换为本地坐标 - * @param worldPos 世界坐标位置 - * @return 本地坐标位置 - */ - Vec2 toLocal(const Vec2& worldPos) const; + /** + * @brief 获取本地变换矩阵 + * @return 本地变换矩阵 + */ + glm::mat4 getLocalTransform() const; - /** - * @brief 获取本地变换矩阵 - * @return 本地变换矩阵 - */ - glm::mat4 getLocalTransform() const; + /** + * @brief 获取世界变换矩阵 + * @return 世界变换矩阵 + */ + glm::mat4 getWorldTransform() const; - /** - * @brief 获取世界变换矩阵 - * @return 世界变换矩阵 - */ - glm::mat4 getWorldTransform() const; + /** + * @brief 标记变换矩阵为脏状态,并传播到所有子节点 + */ + void markTransformDirty(); - /** - * @brief 标记变换矩阵为脏状态,并传播到所有子节点 - */ - void markTransformDirty(); + /** + * @brief 批量更新变换矩阵 + */ + void batchTransforms(); - /** - * @brief 批量更新变换矩阵 - */ - void batchTransforms(); + /** + * @brief 获取变换脏标记状态 + */ + bool isTransformDirty() const; + bool isWorldTransformDirty() const; - /** - * @brief 获取变换脏标记状态 - */ - bool isTransformDirty() const; - bool isWorldTransformDirty() const; + // ------------------------------------------------------------------------- + // 可见性和外观 + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------- - // 可见性和外观 - // ------------------------------------------------------------------------- + /** + * @brief 设置透明度 + * @param opacity 透明度值(0.0-1.0范围) + */ + void setOpacity(float opacity); - /** - * @brief 设置透明度 - * @param opacity 透明度值(0.0-1.0范围) - */ - void setOpacity(float opacity); + /** + * @brief 获取透明度 + * @return 透明度值 + */ + float getOpacity() const { return opacity_; } - /** - * @brief 获取透明度 - * @return 透明度值 - */ - float getOpacity() const { return opacity_; } + /** + * @brief 设置可见性 + * @param visible 是否可见 + */ + void setVisible(bool visible); - /** - * @brief 设置可见性 - * @param visible 是否可见 - */ - void setVisible(bool visible); + /** + * @brief 获取可见性 + * @return 是否可见 + */ + bool isVisible() const { return visible_; } - /** - * @brief 获取可见性 - * @return 是否可见 - */ - bool isVisible() const { return visible_; } + /** + * @brief 设置颜色 + * @param color RGB颜色 + */ + void setColor(const Color3B &color); - /** - * @brief 设置颜色 - * @param color RGB颜色 - */ - void setColor(const Color3B& color); + /** + * @brief 获取颜色 + * @return RGB颜色 + */ + Color3B getColor() const { return color_; } - /** - * @brief 获取颜色 - * @return RGB颜色 - */ - Color3B getColor() const { return color_; } + /** + * @brief 设置X轴翻转 + */ + void setFlipX(bool flipX); - /** - * @brief 设置X轴翻转 - */ - void setFlipX(bool flipX); + /** + * @brief 获取X轴翻转状态 + */ + bool isFlipX() const { return flipX_; } - /** - * @brief 获取X轴翻转状态 - */ - bool isFlipX() const { return flipX_; } + /** + * @brief 设置Y轴翻转 + */ + void setFlipY(bool flipY); - /** - * @brief 设置Y轴翻转 - */ - void setFlipY(bool flipY); + /** + * @brief 获取Y轴翻转状态 + */ + bool isFlipY() const { return flipY_; } - /** - * @brief 获取Y轴翻转状态 - */ - bool isFlipY() const { return flipY_; } + /** + * @brief 设置Z序 + * @param zOrder 渲染层级顺序 + */ + void setZOrder(int zOrder); - /** - * @brief 设置Z序 - * @param zOrder 渲染层级顺序 - */ - void setZOrder(int zOrder); + /** + * @brief 获取Z序 + * @return 渲染层级顺序 + */ + int getZOrder() const { return zOrder_; } - /** - * @brief 获取Z序 - * @return 渲染层级顺序 - */ - int getZOrder() const { return zOrder_; } + // ------------------------------------------------------------------------- + // 名称和标签 + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------- - // 名称和标签 - // ------------------------------------------------------------------------- + /** + * @brief 设置名称 + * @param name 节点名称 + */ + void setName(const std::string &name) { name_ = name; } - /** - * @brief 设置名称 - * @param name 节点名称 - */ - void setName(const std::string& name) { name_ = name; } + /** + * @brief 获取名称 + * @return 节点名称 + */ + const std::string &getName() const { return name_; } - /** - * @brief 获取名称 - * @return 节点名称 - */ - const std::string& getName() const { return name_; } + /** + * @brief 设置标签 + * @param tag 标签值 + */ + void setTag(int tag) { tag_ = tag; } - /** - * @brief 设置标签 - * @param tag 标签值 - */ - void setTag(int tag) { tag_ = tag; } + /** + * @brief 获取标签 + * @return 标签值 + */ + int getTag() const { return tag_; } - /** - * @brief 获取标签 - * @return 标签值 - */ - int getTag() const { return tag_; } + // ------------------------------------------------------------------------- + // 生命周期回调 + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------- - // 生命周期回调 - // ------------------------------------------------------------------------- + /** + * @brief 节点进入时的回调 + */ + virtual void onEnter(); - /** - * @brief 节点进入时的回调 - */ - virtual void onEnter(); + /** + * @brief 节点退出时的回调 + */ + virtual void onExit(); - /** - * @brief 节点退出时的回调 - */ - virtual void onExit(); + /** + * @brief 更新回调 + * @param dt 帧间隔时间(秒) + */ + virtual void onUpdate(float dt); - /** - * @brief 更新回调 - * @param dt 帧间隔时间(秒) - */ - virtual void onUpdate(float dt); + /** + * @brief 渲染回调 + * @param renderer 渲染后端引用 + */ + virtual void onRender(RenderBackend &renderer); - /** - * @brief 渲染回调 - * @param renderer 渲染后端引用 - */ - virtual void onRender(RenderBackend& renderer); + /** + * @brief 附加到场景时的回调 + * @param scene 所属场景指针 + */ + virtual void onAttachToScene(Scene *scene); - /** - * @brief 附加到场景时的回调 - * @param scene 所属场景指针 - */ - virtual void onAttachToScene(Scene* scene); + /** + * @brief 从场景分离时的回调 + */ + virtual void onDetachFromScene(); - /** - * @brief 从场景分离时的回调 - */ - virtual void onDetachFromScene(); + // ------------------------------------------------------------------------- + // 边界框 + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------- - // 边界框 - // ------------------------------------------------------------------------- + /** + * @brief 获取节点边界矩形 + * @return 节点的边界矩形 + */ + virtual Rect getBounds() const; - /** - * @brief 获取节点边界矩形 - * @return 节点的边界矩形 - */ - virtual Rect getBounds() const; + // ------------------------------------------------------------------------- + // 事件系统 + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------- - // 事件系统 - // ------------------------------------------------------------------------- + /** + * @brief 获取事件分发器 + * @return 事件分发器引用 + */ + EventDispatcher &getEventDispatcher() { return eventDispatcher_; } - /** - * @brief 获取事件分发器 - * @return 事件分发器引用 - */ - EventDispatcher& getEventDispatcher() { return eventDispatcher_; } + // ------------------------------------------------------------------------- + // 内部方法 + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------- - // 内部方法 - // ------------------------------------------------------------------------- + /** + * @brief 更新节点 + * @param dt 帧间隔时间(秒) + */ + void update(float dt); - /** - * @brief 更新节点 - * @param dt 帧间隔时间(秒) - */ - void update(float dt); + /** + * @brief 渲染节点 + * @param renderer 渲染后端引用 + */ + void render(RenderBackend &renderer); - /** - * @brief 渲染节点 - * @param renderer 渲染后端引用 - */ - void render(RenderBackend& renderer); + /** + * @brief 对子节点排序 + */ + void sortChildren(); - /** - * @brief 对子节点排序 - */ - void sortChildren(); + /** + * @brief 检查是否正在运行 + * @return 是否正在运行 + */ + bool isRunning() const { return running_; } - /** - * @brief 检查是否正在运行 - * @return 是否正在运行 - */ - bool isRunning() const { return running_; } + /** + * @brief 获取所属场景 + * @return 所属场景指针 + */ + Scene *getScene() const { return scene_; } - /** - * @brief 获取所属场景 - * @return 所属场景指针 - */ - Scene* getScene() const { return scene_; } + /** + * @brief 收集渲染命令 + * @param commands 渲染命令输出向量 + * @param parentZOrder 父节点的Z序 + */ + virtual void collectRenderCommands(std::vector &commands, + int parentZOrder = 0); - /** - * @brief 收集渲染命令 - * @param commands 渲染命令输出向量 - * @param parentZOrder 父节点的Z序 - */ - virtual void collectRenderCommands(std::vector& commands, - int parentZOrder = 0); - - /** - * @brief 收集渲染命令到队列 - * @param queue 渲染队列引用 - */ - virtual void collectRenderCommandsToQueue(RenderQueue& queue); + /** + * @brief 收集渲染命令到队列 + * @param queue 渲染队列引用 + */ + virtual void collectRenderCommandsToQueue(RenderQueue &queue); protected: - /** - * @brief 绘制回调(子类重写) - * @param renderer 渲染后端引用 - */ - virtual void onDraw(RenderBackend& renderer) {} + /** + * @brief 绘制回调(子类重写) + * @param renderer 渲染后端引用 + */ + virtual void onDraw(RenderBackend &renderer) {} - /** - * @brief 节点更新回调(子类重写) - * @param dt 帧间隔时间(秒) - */ - virtual void onUpdateNode(float dt) {} + /** + * @brief 节点更新回调(子类重写) + * @param dt 帧间隔时间(秒) + */ + virtual void onUpdateNode(float dt) {} - /** - * @brief 生成渲染命令(子类重写) - * @param commands 渲染命令输出向量 - * @param zOrder Z序 - */ - virtual void generateRenderCommand(std::vector& commands, - int zOrder) {} + /** + * @brief 生成渲染命令(子类重写) + * @param commands 渲染命令输出向量 + * @param zOrder Z序 + */ + virtual void generateRenderCommand(std::vector &commands, + int zOrder) {} - /** - * @brief 生成渲染命令到队列(子类重写) - * @param queue 渲染队列引用 - */ - virtual void generateRenderCommandToQueue(RenderQueue& queue) { (void)queue; } + /** + * @brief 生成渲染命令到队列(子类重写) + * @param queue 渲染队列引用 + */ + virtual void generateRenderCommandToQueue(RenderQueue &queue) { (void)queue; } private: - // ========================================================================== - // 成员变量按类型大小降序排列,减少内存对齐填充 - // ========================================================================== + // ========================================================================== + // 成员变量按类型大小降序排列,减少内存对齐填充 + // ========================================================================== - // 1. 大块内存(64字节) - mutable glm::mat4 localTransform_ = glm::mat4(1.0f); - mutable glm::mat4 worldTransform_ = glm::mat4(1.0f); + // 1. 大块内存(64字节) + mutable glm::mat4 localTransform_ = glm::mat4(1.0f); + mutable glm::mat4 worldTransform_ = glm::mat4(1.0f); - // 2. 字符串和容器(24-32字节) - std::string name_; - std::vector> children_; - std::unordered_map> components_; + // 2. 字符串和容器(24-32字节) + std::string name_; + std::vector> children_; + std::unordered_map> components_; - // 3. 子节点索引(加速查找) - std::unordered_map> nameIndex_; - std::unordered_map> tagIndex_; + // 3. 子节点索引(加速查找) + std::unordered_map> nameIndex_; + std::unordered_map> tagIndex_; - // 4. 事件分发器 - EventDispatcher eventDispatcher_; + // 4. 事件分发器 + EventDispatcher eventDispatcher_; - // 5. 父节点引用 - WeakPtr parent_; + // 5. 父节点引用 + WeakPtr parent_; - // 6. 变换组件(内置) - TransformComponent* transform_ = nullptr; + // 6. 变换组件(内置) + TransformComponent *transform_ = nullptr; - // 7. 浮点属性 - float opacity_ = 1.0f; + // 7. 浮点属性 + float opacity_ = 1.0f; - // 8. 颜色属性 - Color3B color_ = Color3B(255, 255, 255); + // 8. 颜色属性 + Color3B color_ = Color3B(255, 255, 255); - // 9. 整数属性 - int zOrder_ = 0; - int tag_ = -1; + // 9. 整数属性 + int zOrder_ = 0; + int tag_ = -1; - // 10. 布尔属性 - bool flipX_ = false; - bool flipY_ = false; + // 10. 布尔属性 + bool flipX_ = false; + bool flipY_ = false; - // 11. 场景指针 - Scene* scene_ = nullptr; + // 11. 场景指针 + Scene *scene_ = nullptr; - // 12. 布尔标志(打包在一起) - mutable bool transformDirty_ = true; - mutable bool worldTransformDirty_ = true; - bool childrenOrderDirty_ = false; - bool visible_ = true; - bool running_ = false; + // 12. 布尔标志(打包在一起) + mutable bool transformDirty_ = true; + mutable bool worldTransformDirty_ = true; + bool childrenOrderDirty_ = false; + bool visible_ = true; + bool running_ = false; }; } // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/scene.h b/Extra2D/include/extra2d/scene/scene.h index 95abfa9..41f40f7 100644 --- a/Extra2D/include/extra2d/scene/scene.h +++ b/Extra2D/include/extra2d/scene/scene.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include @@ -11,187 +11,187 @@ class RenderQueue; /** * @brief 场景类 - 节点容器,管理整个场景图 - * + * * 场景是场景图的根节点,管理相机、视口和渲染流程 */ class Scene : public Node { public: - Scene(); - ~Scene() override = default; + Scene(); + ~Scene() override = default; - // ------------------------------------------------------------------------- - // 场景属性 - // ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // 场景属性 + // ------------------------------------------------------------------------- - /** - * @brief 设置背景颜色 - * @param color 背景颜色 - */ - void setBackgroundColor(const Color& color) { backgroundColor_ = color; } + /** + * @brief 设置背景颜色 + * @param color 背景颜色 + */ + void setBackgroundColor(const Color &color) { backgroundColor_ = color; } - /** - * @brief 获取背景颜色 - * @return 背景颜色 - */ - Color getBackgroundColor() const { return backgroundColor_; } + /** + * @brief 获取背景颜色 + * @return 背景颜色 + */ + Color getBackgroundColor() const { return backgroundColor_; } - // ------------------------------------------------------------------------- - // 摄像机 - // ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // 摄像机 + // ------------------------------------------------------------------------- - /** - * @brief 设置场景相机 - * @param camera 相机智能指针 - */ - void setCamera(Ptr camera); + /** + * @brief 设置场景相机 + * @param camera 相机智能指针 + */ + void setCamera(Ptr camera); - /** - * @brief 获取场景相机 - * @return 相机智能指针 - */ - Ptr getCamera() const { return camera_; } + /** + * @brief 获取场景相机 + * @return 相机智能指针 + */ + Ptr getCamera() const { return camera_; } - /** - * @brief 获取活动相机 - * @return 活动相机指针(优先返回设置的相机,否则返回默认相机) - */ - Camera* getActiveCamera() const { - return camera_ ? camera_.get() : defaultCamera_.get(); - } + /** + * @brief 获取活动相机 + * @return 活动相机指针(优先返回设置的相机,否则返回默认相机) + */ + Camera *getActiveCamera() const { + return camera_ ? camera_.get() : defaultCamera_.get(); + } - // ------------------------------------------------------------------------- - // 视口和尺寸 - // ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // 视口和尺寸 + // ------------------------------------------------------------------------- - /** - * @brief 设置视口大小 - * @param width 视口宽度 - * @param height 视口高度 - */ - void setViewportSize(float width, float height); + /** + * @brief 设置视口大小 + * @param width 视口宽度 + * @param height 视口高度 + */ + void setViewportSize(float width, float height); - /** - * @brief 设置视口大小 - * @param size 视口尺寸结构体 - */ - void setViewportSize(const Size& size); + /** + * @brief 设置视口大小 + * @param size 视口尺寸结构体 + */ + void setViewportSize(const Size &size); - /** - * @brief 获取视口大小 - * @return 视口尺寸 - */ - Size getViewportSize() const { return viewportSize_; } + /** + * @brief 获取视口大小 + * @return 视口尺寸 + */ + Size getViewportSize() const { return viewportSize_; } - /** - * @brief 获取视口宽度 - * @return 视口宽度 - */ - float getWidth() const { return viewportSize_.width; } + /** + * @brief 获取视口宽度 + * @return 视口宽度 + */ + float getWidth() const { return viewportSize_.width; } - /** - * @brief 获取视口高度 - * @return 视口高度 - */ - float getHeight() const { return viewportSize_.height; } + /** + * @brief 获取视口高度 + * @return 视口高度 + */ + float getHeight() const { return viewportSize_.height; } - // ------------------------------------------------------------------------- - // 场景状态 - // ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // 场景状态 + // ------------------------------------------------------------------------- - /** - * @brief 检查场景是否暂停 - * @return 如果场景暂停返回true - */ - bool isPaused() const { return paused_; } + /** + * @brief 检查场景是否暂停 + * @return 如果场景暂停返回true + */ + bool isPaused() const { return paused_; } - /** - * @brief 暂停场景 - */ - void pause() { paused_ = true; } + /** + * @brief 暂停场景 + */ + void pause() { paused_ = true; } - /** - * @brief 恢复场景 - */ - void resume() { paused_ = false; } + /** + * @brief 恢复场景 + */ + void resume() { paused_ = false; } - // ------------------------------------------------------------------------- - // 渲染和更新 - // ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // 渲染和更新 + // ------------------------------------------------------------------------- - /** - * @brief 渲染场景 - * @param renderer 渲染后端引用 - */ - void renderScene(RenderBackend& renderer); + /** + * @brief 渲染场景 + * @param renderer 渲染后端引用 + */ + void renderScene(RenderBackend &renderer); - /** - * @brief 渲染场景内容 - * @param renderer 渲染后端引用 - */ - virtual void renderContent(RenderBackend& renderer); + /** + * @brief 渲染场景内容 + * @param renderer 渲染后端引用 + */ + virtual void renderContent(RenderBackend &renderer); - /** - * @brief 更新场景 - * @param dt 帧间隔时间(秒) - */ - void updateScene(float dt); + /** + * @brief 更新场景 + * @param dt 帧间隔时间(秒) + */ + void updateScene(float dt); - /** - * @brief 收集渲染命令 - * @param commands 渲染命令输出向量 - * @param parentZOrder 父节点的Z序 - */ - void collectRenderCommands(std::vector& commands, - int parentZOrder = 0) override; + /** + * @brief 收集渲染命令 + * @param commands 渲染命令输出向量 + * @param parentZOrder 父节点的Z序 + */ + void collectRenderCommands(std::vector &commands, + int parentZOrder = 0) override; - /** - * @brief 收集渲染命令到队列 - * @param queue 渲染队列引用 - */ - void collectRenderCommandsToQueue(RenderQueue& queue) override; + /** + * @brief 收集渲染命令到队列 + * @param queue 渲染队列引用 + */ + void collectRenderCommandsToQueue(RenderQueue &queue) override; - // ------------------------------------------------------------------------- - // 静态创建方法 - // ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // 静态创建方法 + // ------------------------------------------------------------------------- - /** - * @brief 创建场景对象 - * @return 新创建的场景智能指针 - */ - static Ptr create(); + /** + * @brief 创建场景对象 + * @return 新创建的场景智能指针 + */ + static Ptr create(); protected: - /** - * @brief 场景进入时的回调 - */ - void onEnter() override; + /** + * @brief 场景进入时的回调 + */ + void onEnter() override; - /** - * @brief 场景退出时的回调 - */ - void onExit() override; + /** + * @brief 场景退出时的回调 + */ + void onExit() override; - /** - * @brief 过渡场景开始时的回调(供 TransitionScene 使用) - */ - virtual void onExitTransitionDidStart() {} + /** + * @brief 过渡场景开始时的回调(供 TransitionScene 使用) + */ + virtual void onExitTransitionDidStart() {} - /** - * @brief 过渡场景结束时的回调(供 TransitionScene 使用) - */ - virtual void onEnterTransitionDidFinish() {} + /** + * @brief 过渡场景结束时的回调(供 TransitionScene 使用) + */ + virtual void onEnterTransitionDidFinish() {} - friend class SceneManager; - friend class TransitionScene; + friend class SceneManager; + friend class TransitionScene; private: - Color backgroundColor_ = Colors::Black; - Size viewportSize_ = Size::Zero(); + Color backgroundColor_ = Colors::Black; + Size viewportSize_ = Size::Zero(); - Ptr camera_; - Ptr defaultCamera_; + Ptr camera_; + Ptr defaultCamera_; - bool paused_ = false; + bool paused_ = false; }; } // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/shape_node.h b/Extra2D/include/extra2d/scene/shape_node.h index e0af481..3e0caa3 100644 --- a/Extra2D/include/extra2d/scene/shape_node.h +++ b/Extra2D/include/extra2d/scene/shape_node.h @@ -2,16 +2,12 @@ #include #include +#include #include #include namespace extra2d { -// ============================================================================ -// 形状类型 -// ============================================================================ -enum class ShapeType { Point, Line, Rect, Circle, Triangle, Polygon }; - // ============================================================================ // 形状节点 - 用于绘制几何形状 // ============================================================================ diff --git a/Extra2D/include/extra2d/scene/sprite.h b/Extra2D/include/extra2d/scene/sprite.h index 37c48ea..915e1ee 100644 --- a/Extra2D/include/extra2d/scene/sprite.h +++ b/Extra2D/include/extra2d/scene/sprite.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include namespace extra2d { diff --git a/Extra2D/include/extra2d/services/camera_service.h b/Extra2D/include/extra2d/services/camera_service.h index 81ab0cb..ece8a9c 100644 --- a/Extra2D/include/extra2d/services/camera_service.h +++ b/Extra2D/include/extra2d/services/camera_service.h @@ -2,8 +2,8 @@ #include #include -#include -#include +#include +#include #include namespace extra2d { diff --git a/Extra2D/src/app/application.cpp b/Extra2D/src/app/application.cpp index 7b199d8..c0b727b 100644 --- a/Extra2D/src/app/application.cpp +++ b/Extra2D/src/app/application.cpp @@ -1,19 +1,19 @@ #include #include -#include -#include -#include #include #include #include #include +#include +#include +#include +#include #include #include #include #include #include - namespace extra2d { static double getTimeSeconds() { @@ -81,9 +81,9 @@ void Application::configureCameraService() { static_cast(win->height()), 0); ViewportConfig vpConfig; - vpConfig.logicWidth = static_cast(win->width()); - vpConfig.logicHeight = static_cast(win->height()); - vpConfig.mode = ViewportMode::AspectRatio; + vpConfig.designWidth = static_cast(win->width()); + vpConfig.designHeight = static_cast(win->height()); + vpConfig.scaleMode = ViewportScaleMode::Letterbox; cameraService->setViewportConfig(vpConfig); cameraService->updateViewport(win->width(), win->height()); @@ -97,8 +97,6 @@ void Application::shutdown() { if (!initialized_) return; - VRAMMgr::get().printStats(); - ServiceLocator::instance().shutdownAll(); ServiceLocator::instance().clear(); Registry::instance().shutdown(); @@ -176,8 +174,7 @@ void Application::mainLoop() { // 帧率限制 auto *renderMod = get(); - if (renderMod && renderMod->renderer()) { - // 这里可以添加帧率限制逻辑 + if (renderMod && renderMod->getRenderer()) { } } @@ -192,29 +189,28 @@ void Application::update() { void Application::render() { auto *renderMod = get(); - if (!renderMod || !renderMod->renderer()) + if (!renderMod || !renderMod->getRenderer()) return; - auto *renderer = renderMod->renderer(); + auto *renderer = renderMod->getRenderer(); auto *winMod = get(); if (!winMod || !winMod->win()) return; auto cameraService = ServiceLocator::instance().getService(); if (cameraService) { - const auto &vp = cameraService->getViewportResult().viewport; + const auto &vpResult = cameraService->getViewportResult(); renderer->setViewport( - static_cast(vp.origin.x), static_cast(vp.origin.y), - static_cast(vp.size.width), static_cast(vp.size.height)); - renderer->setViewProjection(cameraService->getViewProjectionMatrix()); + vpResult.offsetX, vpResult.offsetY, + static_cast(vpResult.width), static_cast(vpResult.height)); } else { renderer->setViewport(0, 0, winMod->win()->width(), winMod->win()->height()); } auto sceneService = ServiceLocator::instance().getService(); - if (sceneService) { - sceneService->render(*renderer); + if (sceneService && renderer->getBackend()) { + sceneService->render(*renderer->getBackend()); } winMod->win()->swap(); @@ -227,7 +223,7 @@ IWindow *Application::window() { RenderBackend *Application::renderer() { auto *renderMod = get(); - return renderMod ? renderMod->renderer() : nullptr; + return renderMod ? (renderMod->getRenderer() ? renderMod->getRenderer()->getBackend() : nullptr) : nullptr; } IInput *Application::input() { diff --git a/Extra2D/src/render/backends/opengl/gl_context.cpp b/Extra2D/src/render/backends/opengl/gl_context.cpp new file mode 100644 index 0000000..d17ccbf --- /dev/null +++ b/Extra2D/src/render/backends/opengl/gl_context.cpp @@ -0,0 +1,128 @@ +#include + +#include +#include + +namespace extra2d { + +GLContext& GLContext::get() { + static GLContext instance; + return instance; +} + +bool GLContext::init() { + if (initialized_) { + return true; + } + + if (!gladLoadGL()) { + E2D_LOG_ERROR("Failed to load OpenGL functions"); + return false; + } + + parseVersion(); + queryCapabilities(); + + initialized_ = true; + + E2D_LOG_INFO("OpenGL Context initialized"); + E2D_LOG_INFO(" Version: {}.{}", version_.major, version_.minor); + E2D_LOG_INFO(" Renderer: {}", renderer_); + E2D_LOG_INFO(" Vendor: {}", vendor_); + E2D_LOG_INFO(" GLSL: {}", glslVersion_); + + return true; +} + +void GLContext::shutdown() { + if (!initialized_) { + return; + } + + extensions_.clear(); + initialized_ = false; + + E2D_LOG_INFO("OpenGL Context shutdown"); +} + +std::string GLContext::getVersionString() const { + std::ostringstream oss; + oss << version_.major << "." << version_.minor; + return oss.str(); +} + +bool GLContext::hasExtension(const std::string& extension) const { + return extensions_.find(extension) != extensions_.end(); +} + +bool GLContext::checkError(const char* operation) { + GLenum error = glGetError(); + if (error != GL_NO_ERROR) { + if (operation) { + E2D_LOG_ERROR("OpenGL error during {}: 0x{:X}", operation, error); + } else { + E2D_LOG_ERROR("OpenGL error: 0x{:X}", error); + } + return false; + } + return true; +} + +void GLContext::clearErrors() { + while (glGetError() != GL_NO_ERROR) { + } +} + +void GLContext::queryCapabilities() { + const char* rendererStr = reinterpret_cast(glGetString(GL_RENDERER)); + const char* vendorStr = reinterpret_cast(glGetString(GL_VENDOR)); + const char* glslStr = reinterpret_cast(glGetString(GL_SHADING_LANGUAGE_VERSION)); + + renderer_ = rendererStr ? rendererStr : "Unknown"; + vendor_ = vendorStr ? vendorStr : "Unknown"; + glslVersion_ = glslStr ? glslStr : "Unknown"; + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize_); + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits_); + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs_); + glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings_); + + GLint numExtensions = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); + for (GLint i = 0; i < numExtensions; ++i) { + const char* ext = reinterpret_cast(glGetStringi(GL_EXTENSIONS, i)); + if (ext) { + extensions_.insert(ext); + } + } +} + +void GLContext::parseVersion() { + const char* versionStr = reinterpret_cast(glGetString(GL_VERSION)); + if (!versionStr) { + version_ = {4, 5}; + return; + } + + int major = 0, minor = 0; + const char* ptr = versionStr; + + while (*ptr && (*ptr < '0' || *ptr > '9')) { + ptr++; + } + + if (*ptr) { + major = std::atoi(ptr); + while (*ptr && *ptr != '.') { + ptr++; + } + if (*ptr == '.') { + ptr++; + minor = std::atoi(ptr); + } + } + + version_ = {major, minor}; +} + +} // namespace extra2d diff --git a/Extra2D/src/render/backends/opengl/gl_render_backend.cpp b/Extra2D/src/render/backends/opengl/gl_render_backend.cpp new file mode 100644 index 0000000..6e0aa46 --- /dev/null +++ b/Extra2D/src/render/backends/opengl/gl_render_backend.cpp @@ -0,0 +1,521 @@ +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 构造函数 + */ +GLRenderBackend::GLRenderBackend() + : initialized_(false) + , window_(nullptr) + , config_() + , stats_() + , device_(nullptr) + , projectionMatrix_(1.0f) + , viewMatrix_(1.0f) + , width_(0) + , height_(0) + , sortKey_(0) + , layer_(0) + , blendState_(rhi::BlendState::alphaBlend()) + , inBatch_(false) { +} + +/** + * @brief 析构函数 + */ +GLRenderBackend::~GLRenderBackend() { + shutdown(); +} + +// ============================================================================ +// 生命周期 +// ============================================================================ + +/** + * @brief 初始化渲染后端 + */ +bool GLRenderBackend::init(IWindow *window, const RenderBackendConfig &config) { + if (initialized_) { + return true; + } + + if (!window) { + return false; + } + + window_ = window; + config_ = config; + width_ = window->width(); + height_ = window->height(); + + device_ = rhi::RHIDevice::create(rhi::GraphicsAPI::OpenGL); + if (!device_) { + return false; + } + + if (!device_->init(window)) { + device_.reset(); + return false; + } + + projectionMatrix_ = glm::ortho(0.0f, static_cast(width_), + static_cast(height_), 0.0f, + -1.0f, 1.0f); + viewMatrix_ = glm::mat4(1.0f); + + initialized_ = true; + return true; +} + +/** + * @brief 关闭渲染后端 + */ +void GLRenderBackend::shutdown() { + if (!initialized_) { + return; + } + + if (device_) { + device_->shutdown(); + device_.reset(); + } + + window_ = nullptr; + width_ = 0; + height_ = 0; + initialized_ = false; +} + +/** + * @brief 检查是否已初始化 + */ +bool GLRenderBackend::isValid() const { + return initialized_ && device_ && device_->isValid(); +} + +// ============================================================================ +// 帧管理 +// ============================================================================ + +/** + * @brief 开始帧渲染 + */ +void GLRenderBackend::beginFrame(const Color &clearColor) { + if (!isValid()) { + return; + } + + stats_.reset(); + device_->beginFrame(); + device_->resetStats(); + + rhi::ColorValue cv(clearColor.r, clearColor.g, clearColor.b, clearColor.a); + device_->clearColor(cv); + + device_->setViewport(rhi::Viewport(0.0f, 0.0f, + static_cast(width_), + static_cast(height_))); +} + +/** + * @brief 结束帧渲染 + */ +void GLRenderBackend::endFrame() { + if (!isValid()) { + return; + } + + if (inBatch_) { + endSpriteBatch(); + } + + device_->endFrame(); +} + +/** + * @brief 呈现帧 + */ +void GLRenderBackend::present() { + if (!isValid()) { + return; + } + + device_->present(); +} + +// ============================================================================ +// 渲染状态 +// ============================================================================ + +/** + * @brief 设置视口 + */ +void GLRenderBackend::setViewport(int x, int y, int width, int height) { + if (!isValid()) { + return; + } + + device_->setViewport(rhi::Viewport( + static_cast(x), + static_cast(y), + static_cast(width), + static_cast(height))); +} + +/** + * @brief 设置裁剪区域 + */ +void GLRenderBackend::setScissor(int x, int y, int width, int height) { + if (!isValid()) { + return; + } + + device_->setScissorRect(rhi::ScissorRect(x, y, + static_cast(width), + static_cast(height))); +} + +/** + * @brief 启用/禁用裁剪测试 + */ +void GLRenderBackend::setScissorEnabled(bool enabled) { + if (!isValid()) { + return; + } + + device_->setScissorEnabled(enabled); +} + +/** + * @brief 设置投影矩阵 + */ +void GLRenderBackend::setProjectionMatrix(const glm::mat4 &matrix) { + projectionMatrix_ = matrix; +} + +/** + * @brief 设置视图矩阵 + */ +void GLRenderBackend::setViewMatrix(const glm::mat4 &matrix) { + viewMatrix_ = matrix; +} + +/** + * @brief 设置垂直同步 + */ +void GLRenderBackend::setVSync(bool enabled) { + if (!isValid()) { + return; + } + + device_->setVSync(enabled); +} + +/** + * @brief 获取垂直同步状态 + */ +bool GLRenderBackend::isVSyncEnabled() const { + return isValid() ? device_->isVSyncEnabled() : false; +} + +// ============================================================================ +// 精灵渲染 +// ============================================================================ + +/** + * @brief 绘制精灵 + */ +void GLRenderBackend::drawSprite(Ptr texture, const Rect &destRect, + const Rect &srcRect, const Color &color, + float rotation, bool flipX, bool flipY) { + if (!isValid() || !texture) { + return; + } + + stats_.spriteCount++; + stats_.drawCalls++; +} + +/** + * @brief 绘制精灵(使用变换矩阵) + */ +void GLRenderBackend::drawSprite(Ptr texture, + const glm::mat4 &transform, + const Rect &srcRect, const Color &color) { + if (!isValid() || !texture) { + return; + } + + stats_.spriteCount++; + stats_.drawCalls++; +} + +/** + * @brief 绘制九宫格精灵 + */ +void GLRenderBackend::draw9Slice(Ptr texture, const Rect &destRect, + const Rect &srcRect, const Vec2 &borderSize, + const Color &color) { + if (!isValid() || !texture) { + return; + } + + stats_.spriteCount += 9; + stats_.drawCalls++; +} + +// ============================================================================ +// 文本渲染 +// ============================================================================ + +/** + * @brief 绘制文本 + */ +void GLRenderBackend::drawText(const std::string &text, const Vec2 &position, + float fontSize, const Color &color) { + if (!isValid() || text.empty()) { + return; + } + + stats_.textCharCount += static_cast(text.length()); + stats_.drawCalls++; +} + +/** + * @brief 绘制文本(UTF-32) + */ +void GLRenderBackend::drawText(const std::u32string &text, const Vec2 &position, + float fontSize, const Color &color) { + if (!isValid() || text.empty()) { + return; + } + + stats_.textCharCount += static_cast(text.length()); + stats_.drawCalls++; +} + +/** + * @brief 测量文本尺寸 + */ +Vec2 GLRenderBackend::measureText(const std::string &text, float fontSize) { + float width = static_cast(text.length()) * fontSize * 0.6f; + return Vec2(width, fontSize); +} + +/** + * @brief 测量文本尺寸(UTF-32) + */ +Vec2 GLRenderBackend::measureText(const std::u32string &text, float fontSize) { + float width = static_cast(text.length()) * fontSize * 0.6f; + return Vec2(width, fontSize); +} + +// ============================================================================ +// 形状渲染 +// ============================================================================ + +/** + * @brief 绘制矩形 + */ +void GLRenderBackend::drawRect(const Rect &rect, const Color &color, float lineWidth) { + if (!isValid()) { + return; + } + + stats_.shapeCount++; + stats_.drawCalls++; +} + +/** + * @brief 绘制填充矩形 + */ +void GLRenderBackend::fillRect(const Rect &rect, const Color &color) { + if (!isValid()) { + return; + } + + stats_.shapeCount++; + stats_.drawCalls++; +} + +/** + * @brief 绘制圆形 + */ +void GLRenderBackend::drawCircle(const Vec2 ¢er, float radius, const Color &color, + float lineWidth, uint32_t segments) { + if (!isValid()) { + return; + } + + stats_.shapeCount++; + stats_.drawCalls++; +} + +/** + * @brief 绘制填充圆形 + */ +void GLRenderBackend::fillCircle(const Vec2 ¢er, float radius, const Color &color, + uint32_t segments) { + if (!isValid()) { + return; + } + + stats_.shapeCount++; + stats_.drawCalls++; +} + +/** + * @brief 绘制线段 + */ +void GLRenderBackend::drawLine(const Vec2 &start, const Vec2 &end, const Color &color, + float lineWidth) { + if (!isValid()) { + return; + } + + stats_.shapeCount++; + stats_.drawCalls++; +} + +/** + * @brief 绘制多边形 + */ +void GLRenderBackend::drawPolygon(const Vec2 *points, size_t count, const Color &color, + float lineWidth) { + if (!isValid() || !points || count < 3) { + return; + } + + stats_.shapeCount++; + stats_.drawCalls++; +} + +/** + * @brief 绘制填充多边形 + */ +void GLRenderBackend::fillPolygon(const Vec2 *points, size_t count, const Color &color) { + if (!isValid() || !points || count < 3) { + return; + } + + stats_.shapeCount++; + stats_.drawCalls++; +} + +// ============================================================================ +// 批处理控制 +// ============================================================================ + +/** + * @brief 开始精灵批处理 + */ +void GLRenderBackend::beginSpriteBatch() { + inBatch_ = true; +} + +/** + * @brief 结束精灵批处理 + */ +void GLRenderBackend::endSpriteBatch() { + if (!inBatch_) { + return; + } + + flush(); + inBatch_ = false; +} + +/** + * @brief 刷新当前批次 + */ +void GLRenderBackend::flush() { + if (!isValid()) { + return; + } + + stats_.batchCount++; +} + +/** + * @brief 设置渲染排序键 + */ +void GLRenderBackend::setSortKey(uint64_t key) { + sortKey_ = key; +} + +/** + * @brief 设置渲染层 + */ +void GLRenderBackend::setLayer(int layer) { + layer_ = layer; +} + +/** + * @brief 设置混合模式 + */ +void GLRenderBackend::setBlendMode(rhi::BlendState blend) { + blendState_ = blend; +} + +// ============================================================================ +// 访问器 +// ============================================================================ + +/** + * @brief 获取 RHI 设备 + */ +rhi::RHIDevice *GLRenderBackend::getDevice() const { + return device_.get(); +} + +/** + * @brief 获取窗口宽度 + */ +int GLRenderBackend::getWidth() const { + return width_; +} + +/** + * @brief 获取窗口高度 + */ +int GLRenderBackend::getHeight() const { + return height_; +} + +/** + * @brief 获取渲染统计 + */ +const RenderStats &GLRenderBackend::getStats() const { + return stats_; +} + +/** + * @brief 重置渲染统计 + */ +void GLRenderBackend::resetStats() { + stats_.reset(); + if (device_) { + device_->resetStats(); + } +} + +/** + * @brief 获取图形 API 类型 + */ +rhi::GraphicsAPI GLRenderBackend::getAPI() const { + return rhi::GraphicsAPI::OpenGL; +} + +/** + * @brief 获取设备能力 + */ +const rhi::DeviceCaps &GLRenderBackend::getCaps() const { + static rhi::DeviceCaps empty; + return device_ ? device_->getCaps() : empty; +} + +} // namespace extra2d diff --git a/Extra2D/src/render/backends/opengl/gl_rhi_device.cpp b/Extra2D/src/render/backends/opengl/gl_rhi_device.cpp index 8896805..02914c3 100644 --- a/Extra2D/src/render/backends/opengl/gl_rhi_device.cpp +++ b/Extra2D/src/render/backends/opengl/gl_rhi_device.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/Extra2D/src/render/batch/shape_batcher.cpp b/Extra2D/src/render/batch/shape_batcher.cpp index 20788c5..aeb2e11 100644 --- a/Extra2D/src/render/batch/shape_batcher.cpp +++ b/Extra2D/src/render/batch/shape_batcher.cpp @@ -1,562 +1,575 @@ +#include #include #include #include -#include namespace extra2d { ShapeBatcher::ShapeBatcher() - : device_(nullptr), shader_(nullptr), pipeline_(nullptr), linePipeline_(nullptr), - vertexBuffer_(nullptr), indexBuffer_(nullptr), uniformBuffer_(nullptr), - viewProj_(1.0f), currentBlend_(rhi::BlendState::alphaBlend()), - currentVertexIndex_(0), currentIndexIndex_(0), - drawCallCount_(0), shapeCount_(0), batchCount_(0), - initialized_(false), inBatch_(false) { + : device_(nullptr), shader_(nullptr), pipeline_(nullptr), + linePipeline_(nullptr), vertexBuffer_(nullptr), indexBuffer_(nullptr), + uniformBuffer_(nullptr), viewProj_(1.0f), + currentBlend_(rhi::BlendState::alphaBlend()), currentVertexIndex_(0), + currentIndexIndex_(0), drawCallCount_(0), shapeCount_(0), batchCount_(0), + initialized_(false), inBatch_(false) {} + +ShapeBatcher::ShapeBatcher(const ShapeBatcherConfig &config) : ShapeBatcher() { + config_ = config; } -ShapeBatcher::ShapeBatcher(const ShapeBatcherConfig& config) - : ShapeBatcher() { - config_ = config; -} - -ShapeBatcher::~ShapeBatcher() { - shutdown(); -} +ShapeBatcher::~ShapeBatcher() { shutdown(); } bool ShapeBatcher::init(Ptr device) { - if (initialized_) { - return true; - } - - if (!device) { - return false; - } - - device_ = device; - - if (!createShader()) { - return false; - } - - if (!createPipeline()) { - return false; - } - - if (!createBuffers()) { - return false; - } - - viewProj_ = glm::ortho(0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f); - - initialized_ = true; + if (initialized_) { return true; + } + + if (!device) { + return false; + } + + device_ = device; + + if (!createShader()) { + return false; + } + + if (!createPipeline()) { + return false; + } + + if (!createBuffers()) { + return false; + } + + viewProj_ = glm::ortho(0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f); + + initialized_ = true; + return true; } void ShapeBatcher::shutdown() { - if (!initialized_) { - return; - } + if (!initialized_) { + return; + } - vertices_.clear(); - indices_.clear(); - batches_.clear(); + vertices_.clear(); + indices_.clear(); + batches_.clear(); - uniformBuffer_.reset(); - indexBuffer_.reset(); - vertexBuffer_.reset(); - linePipeline_.reset(); - pipeline_.reset(); - shader_.reset(); - device_.reset(); + uniformBuffer_.reset(); + indexBuffer_.reset(); + vertexBuffer_.reset(); + linePipeline_.reset(); + pipeline_.reset(); + shader_.reset(); + device_.reset(); - initialized_ = false; - inBatch_ = false; + initialized_ = false; + inBatch_ = false; } -void ShapeBatcher::begin() { - begin(viewProj_); -} +void ShapeBatcher::begin() { begin(viewProj_); } -void ShapeBatcher::begin(const glm::mat4& viewProj) { - if (!initialized_ || inBatch_) { - return; - } +void ShapeBatcher::begin(const glm::mat4 &viewProj) { + if (!initialized_ || inBatch_) { + return; + } - viewProj_ = viewProj; - currentVertexIndex_ = 0; - currentIndexIndex_ = 0; - currentBlend_ = rhi::BlendState::alphaBlend(); + viewProj_ = viewProj; + currentVertexIndex_ = 0; + currentIndexIndex_ = 0; + currentBlend_ = rhi::BlendState::alphaBlend(); - vertices_.clear(); - indices_.clear(); - batches_.clear(); + vertices_.clear(); + indices_.clear(); + batches_.clear(); - vertices_.reserve(config_.maxBatchSize * 4); - indices_.reserve(config_.maxBatchSize * 6); + vertices_.reserve(config_.maxBatchSize * 4); + indices_.reserve(config_.maxBatchSize * 6); - inBatch_ = true; + inBatch_ = true; } void ShapeBatcher::end() { - if (!initialized_ || !inBatch_) { - return; - } + if (!initialized_ || !inBatch_) { + return; + } - if (!vertices_.empty()) { - flushBatch(rhi::PrimitiveType::Triangles); - } + if (!vertices_.empty()) { + flushBatch(rhi::PrimitiveType::Triangles); + } - inBatch_ = false; + inBatch_ = false; } -void ShapeBatcher::drawLine(const Vec2& start, const Vec2& end, - const Color& color, float width) { - if (!initialized_ || !inBatch_) { - return; - } +void ShapeBatcher::drawLine(const Vec2 &start, const Vec2 &end, + const Color &color, float width) { + if (!initialized_ || !inBatch_) { + return; + } - uint32_t i0 = addVertex(start, color); - uint32_t i1 = addVertex(end, color); + uint32_t i0 = addVertex(start, color); + uint32_t i1 = addVertex(end, color); - addIndex(i0); - addIndex(i1); + addIndex(i0); + addIndex(i1); - shapeCount_++; + shapeCount_++; } -void ShapeBatcher::drawLines(const std::vector& points, - const Color& color, float width) { - if (!initialized_ || !inBatch_ || points.size() < 2) { - return; - } +void ShapeBatcher::drawLines(const std::vector &points, + const Color &color, float width) { + if (!initialized_ || !inBatch_ || points.size() < 2) { + return; + } - for (size_t i = 0; i + 1 < points.size(); i += 2) { - drawLine(points[i], points[i + 1], color, width); - } + for (size_t i = 0; i + 1 < points.size(); i += 2) { + drawLine(points[i], points[i + 1], color, width); + } } -void ShapeBatcher::drawPolyline(const std::vector& points, - const Color& color, float width) { - if (!initialized_ || !inBatch_ || points.size() < 2) { - return; - } +void ShapeBatcher::drawPolyline(const std::vector &points, + const Color &color, float width) { + if (!initialized_ || !inBatch_ || points.size() < 2) { + return; + } - for (size_t i = 0; i + 1 < points.size(); ++i) { - drawLine(points[i], points[i + 1], color, width); - } + for (size_t i = 0; i + 1 < points.size(); ++i) { + drawLine(points[i], points[i + 1], color, width); + } } -void ShapeBatcher::drawRect(const Rect& rect, const Color& color, float width) { - if (!initialized_ || !inBatch_) { - return; - } +void ShapeBatcher::drawRect(const Rect &rect, const Color &color, float width) { + if (!initialized_ || !inBatch_) { + return; + } - Vec2 p1(rect.origin.x, rect.origin.y); - Vec2 p2(rect.origin.x + rect.size.width, rect.origin.y); - Vec2 p3(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height); - Vec2 p4(rect.origin.x, rect.origin.y + rect.size.height); + Vec2 p1(rect.origin.x, rect.origin.y); + Vec2 p2(rect.origin.x + rect.size.width, rect.origin.y); + Vec2 p3(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height); + Vec2 p4(rect.origin.x, rect.origin.y + rect.size.height); - drawLine(p1, p2, color, width); - drawLine(p2, p3, color, width); - drawLine(p3, p4, color, width); - drawLine(p4, p1, color, width); + drawLine(p1, p2, color, width); + drawLine(p2, p3, color, width); + drawLine(p3, p4, color, width); + drawLine(p4, p1, color, width); } -void ShapeBatcher::drawFilledRect(const Rect& rect, const Color& color) { - if (!initialized_ || !inBatch_) { - return; - } +void ShapeBatcher::drawFilledRect(const Rect &rect, const Color &color) { + if (!initialized_ || !inBatch_) { + return; + } - uint32_t i0 = addVertex(Vec2(rect.origin.x, rect.origin.y), color); - uint32_t i1 = addVertex(Vec2(rect.origin.x + rect.size.width, rect.origin.y), color); - uint32_t i2 = addVertex(Vec2(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height), color); - uint32_t i3 = addVertex(Vec2(rect.origin.x, rect.origin.y + rect.size.height), color); + uint32_t i0 = addVertex(Vec2(rect.origin.x, rect.origin.y), color); + uint32_t i1 = + addVertex(Vec2(rect.origin.x + rect.size.width, rect.origin.y), color); + uint32_t i2 = addVertex( + Vec2(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height), + color); + uint32_t i3 = + addVertex(Vec2(rect.origin.x, rect.origin.y + rect.size.height), color); + + addIndex(i0); + addIndex(i1); + addIndex(i2); + addIndex(i0); + addIndex(i2); + addIndex(i3); + + shapeCount_++; +} + +void ShapeBatcher::drawCircle(const Vec2 ¢er, float radius, + const Color &color, int segments, float width) { + if (!initialized_ || !inBatch_ || radius <= 0.0f) { + return; + } + + int segs = segments > 0 ? segments : static_cast(config_.circleSegments); + std::vector vertices; + generateCircleVertices(center, radius, segs, vertices); + + for (int i = 0; i < segs; ++i) { + int next = (i + 1) % segs; + drawLine(vertices[i], vertices[next], color, width); + } +} + +void ShapeBatcher::drawFilledCircle(const Vec2 ¢er, float radius, + const Color &color, int segments) { + if (!initialized_ || !inBatch_ || radius <= 0.0f) { + return; + } + + int segs = segments > 0 ? segments : static_cast(config_.circleSegments); + std::vector vertices; + generateCircleVertices(center, radius, segs, vertices); + + uint32_t centerIdx = addVertex(center, color); + + for (int i = 0; i < segs; ++i) { + uint32_t v0 = addVertex(vertices[i], color); + uint32_t v1 = addVertex(vertices[(i + 1) % segs], color); + + addIndex(centerIdx); + addIndex(v0); + addIndex(v1); + } + + shapeCount_++; +} + +void ShapeBatcher::drawEllipse(const Vec2 ¢er, float radiusX, float radiusY, + const Color &color, int segments, float width) { + if (!initialized_ || !inBatch_ || radiusX <= 0.0f || radiusY <= 0.0f) { + return; + } + + int segs = segments > 0 ? segments : static_cast(config_.circleSegments); + std::vector vertices; + generateEllipseVertices(center, radiusX, radiusY, segs, vertices); + + for (int i = 0; i < segs; ++i) { + int next = (i + 1) % segs; + drawLine(vertices[i], vertices[next], color, width); + } +} + +void ShapeBatcher::drawFilledEllipse(const Vec2 ¢er, float radiusX, + float radiusY, const Color &color, + int segments) { + if (!initialized_ || !inBatch_ || radiusX <= 0.0f || radiusY <= 0.0f) { + return; + } + + int segs = segments > 0 ? segments : static_cast(config_.circleSegments); + std::vector vertices; + generateEllipseVertices(center, radiusX, radiusY, segs, vertices); + + uint32_t centerIdx = addVertex(center, color); + + for (int i = 0; i < segs; ++i) { + uint32_t v0 = addVertex(vertices[i], color); + uint32_t v1 = addVertex(vertices[(i + 1) % segs], color); + + addIndex(centerIdx); + addIndex(v0); + addIndex(v1); + } + + shapeCount_++; +} + +void ShapeBatcher::drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, + const Color &color, float width) { + if (!initialized_ || !inBatch_) { + return; + } + + drawLine(p1, p2, color, width); + drawLine(p2, p3, color, width); + drawLine(p3, p1, color, width); +} + +void ShapeBatcher::drawFilledTriangle(const Vec2 &p1, const Vec2 &p2, + const Vec2 &p3, const Color &color) { + if (!initialized_ || !inBatch_) { + return; + } + + uint32_t i0 = addVertex(p1, color); + uint32_t i1 = addVertex(p2, color); + uint32_t i2 = addVertex(p3, color); + + addIndex(i0); + addIndex(i1); + addIndex(i2); + + shapeCount_++; +} + +void ShapeBatcher::drawPolygon(const std::vector &points, + const Color &color, float width) { + if (!initialized_ || !inBatch_ || points.size() < 3) { + return; + } + + for (size_t i = 0; i < points.size(); ++i) { + size_t next = (i + 1) % points.size(); + drawLine(points[i], points[next], color, width); + } +} + +void ShapeBatcher::drawFilledPolygon(const std::vector &points, + const Color &color) { + if (!initialized_ || !inBatch_ || points.size() < 3) { + return; + } + + uint32_t i0 = addVertex(points[0], color); + + for (size_t i = 1; i + 1 < points.size(); ++i) { + uint32_t i1 = addVertex(points[i], color); + uint32_t i2 = addVertex(points[i + 1], color); addIndex(i0); addIndex(i1); addIndex(i2); - addIndex(i0); - addIndex(i2); - addIndex(i3); + } - shapeCount_++; + shapeCount_++; } -void ShapeBatcher::drawCircle(const Vec2& center, float radius, - const Color& color, int segments, float width) { - if (!initialized_ || !inBatch_ || radius <= 0.0f) { - return; - } +void ShapeBatcher::drawArc(const Vec2 ¢er, float radius, float startAngle, + float endAngle, const Color &color, int segments, + float width) { + if (!initialized_ || !inBatch_ || radius <= 0.0f) { + return; + } - int segs = segments > 0 ? segments : static_cast(config_.circleSegments); - std::vector vertices; - generateCircleVertices(center, radius, segs, vertices); + int segs = segments > 0 ? segments : static_cast(config_.circleSegments); + float angleRange = endAngle - startAngle; + float angleStep = angleRange / static_cast(segs); - for (int i = 0; i < segs; ++i) { - int next = (i + 1) % segs; - drawLine(vertices[i], vertices[next], color, width); - } + for (int i = 0; i < segs; ++i) { + float a0 = startAngle + angleStep * static_cast(i); + float a1 = startAngle + angleStep * static_cast(i + 1); + + Vec2 p0(center.x + radius * std::cos(a0), center.y + radius * std::sin(a0)); + Vec2 p1(center.x + radius * std::cos(a1), center.y + radius * std::sin(a1)); + + drawLine(p0, p1, color, width); + } } -void ShapeBatcher::drawFilledCircle(const Vec2& center, float radius, - const Color& color, int segments) { - if (!initialized_ || !inBatch_ || radius <= 0.0f) { - return; +void ShapeBatcher::draw(const RenderCommand &command) { + if (command.type != RenderCommandType::Shape) { + return; + } + + const auto &data = std::get(command.data); + + switch (data.shapeType) { + case ShapeType::Line: + if (data.points.size() >= 2) { + drawLine(data.points[0], data.points[1], data.color, data.lineWidth); } - - int segs = segments > 0 ? segments : static_cast(config_.circleSegments); - std::vector vertices; - generateCircleVertices(center, radius, segs, vertices); - - uint32_t centerIdx = addVertex(center, color); - - for (int i = 0; i < segs; ++i) { - uint32_t v0 = addVertex(vertices[i], color); - uint32_t v1 = addVertex(vertices[(i + 1) % segs], color); - - addIndex(centerIdx); - addIndex(v0); - addIndex(v1); + break; + case ShapeType::Rect: + if (data.points.size() >= 2) { + Rect rect(data.points[0].x, data.points[0].y, + data.points[1].x - data.points[0].x, + data.points[1].y - data.points[0].y); + drawRect(rect, data.color, data.lineWidth); } - - shapeCount_++; + break; + case ShapeType::FilledRect: + if (data.points.size() >= 2) { + Rect rect(data.points[0].x, data.points[0].y, + data.points[1].x - data.points[0].x, + data.points[1].y - data.points[0].y); + drawFilledRect(rect, data.color); + } + break; + case ShapeType::Circle: + if (data.points.size() >= 1) { + drawCircle(data.points[0], data.radius, data.color, data.segments, + data.lineWidth); + } + break; + case ShapeType::FilledCircle: + if (data.points.size() >= 1) { + drawFilledCircle(data.points[0], data.radius, data.color, data.segments); + } + break; + case ShapeType::Triangle: + if (data.points.size() >= 3) { + drawTriangle(data.points[0], data.points[1], data.points[2], data.color, + data.lineWidth); + } + break; + case ShapeType::FilledTriangle: + if (data.points.size() >= 3) { + drawFilledTriangle(data.points[0], data.points[1], data.points[2], + data.color); + } + break; + case ShapeType::Polygon: + drawPolygon(data.points, data.color, data.lineWidth); + break; + case ShapeType::FilledPolygon: + drawFilledPolygon(data.points, data.color); + break; + } } -void ShapeBatcher::drawEllipse(const Vec2& center, float radiusX, float radiusY, - const Color& color, int segments, float width) { - if (!initialized_ || !inBatch_ || radiusX <= 0.0f || radiusY <= 0.0f) { - return; - } - - int segs = segments > 0 ? segments : static_cast(config_.circleSegments); - std::vector vertices; - generateEllipseVertices(center, radiusX, radiusY, segs, vertices); - - for (int i = 0; i < segs; ++i) { - int next = (i + 1) % segs; - drawLine(vertices[i], vertices[next], color, width); - } -} - -void ShapeBatcher::drawFilledEllipse(const Vec2& center, float radiusX, float radiusY, - const Color& color, int segments) { - if (!initialized_ || !inBatch_ || radiusX <= 0.0f || radiusY <= 0.0f) { - return; - } - - int segs = segments > 0 ? segments : static_cast(config_.circleSegments); - std::vector vertices; - generateEllipseVertices(center, radiusX, radiusY, segs, vertices); - - uint32_t centerIdx = addVertex(center, color); - - for (int i = 0; i < segs; ++i) { - uint32_t v0 = addVertex(vertices[i], color); - uint32_t v1 = addVertex(vertices[(i + 1) % segs], color); - - addIndex(centerIdx); - addIndex(v0); - addIndex(v1); - } - - shapeCount_++; -} - -void ShapeBatcher::drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3, - const Color& color, float width) { - if (!initialized_ || !inBatch_) { - return; - } - - drawLine(p1, p2, color, width); - drawLine(p2, p3, color, width); - drawLine(p3, p1, color, width); -} - -void ShapeBatcher::drawFilledTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3, - const Color& color) { - if (!initialized_ || !inBatch_) { - return; - } - - uint32_t i0 = addVertex(p1, color); - uint32_t i1 = addVertex(p2, color); - uint32_t i2 = addVertex(p3, color); - - addIndex(i0); - addIndex(i1); - addIndex(i2); - - shapeCount_++; -} - -void ShapeBatcher::drawPolygon(const std::vector& points, - const Color& color, float width) { - if (!initialized_ || !inBatch_ || points.size() < 3) { - return; - } - - for (size_t i = 0; i < points.size(); ++i) { - size_t next = (i + 1) % points.size(); - drawLine(points[i], points[next], color, width); - } -} - -void ShapeBatcher::drawFilledPolygon(const std::vector& points, - const Color& color) { - if (!initialized_ || !inBatch_ || points.size() < 3) { - return; - } - - uint32_t i0 = addVertex(points[0], color); - - for (size_t i = 1; i + 1 < points.size(); ++i) { - uint32_t i1 = addVertex(points[i], color); - uint32_t i2 = addVertex(points[i + 1], color); - - addIndex(i0); - addIndex(i1); - addIndex(i2); - } - - shapeCount_++; -} - -void ShapeBatcher::drawArc(const Vec2& center, float radius, - float startAngle, float endAngle, - const Color& color, int segments, float width) { - if (!initialized_ || !inBatch_ || radius <= 0.0f) { - return; - } - - int segs = segments > 0 ? segments : static_cast(config_.circleSegments); - float angleRange = endAngle - startAngle; - float angleStep = angleRange / static_cast(segs); - - for (int i = 0; i < segs; ++i) { - float a0 = startAngle + angleStep * static_cast(i); - float a1 = startAngle + angleStep * static_cast(i + 1); - - Vec2 p0(center.x + radius * std::cos(a0), center.y + radius * std::sin(a0)); - Vec2 p1(center.x + radius * std::cos(a1), center.y + radius * std::sin(a1)); - - drawLine(p0, p1, color, width); - } -} - -void ShapeBatcher::draw(const RenderCommand& command) { - if (command.type != RenderCommandType::Shape) { - return; - } - - const auto& data = std::get(command.data); - - switch (data.shapeType) { - case ShapeType::Line: - if (data.points.size() >= 2) { - drawLine(data.points[0], data.points[1], data.color, data.lineWidth); - } - break; - case ShapeType::Rect: - if (data.points.size() >= 2) { - Rect rect(data.points[0].x, data.points[0].y, - data.points[1].x - data.points[0].x, - data.points[1].y - data.points[0].y); - drawRect(rect, data.color, data.lineWidth); - } - break; - case ShapeType::FilledRect: - if (data.points.size() >= 2) { - Rect rect(data.points[0].x, data.points[0].y, - data.points[1].x - data.points[0].x, - data.points[1].y - data.points[0].y); - drawFilledRect(rect, data.color); - } - break; - case ShapeType::Circle: - if (data.points.size() >= 1) { - drawCircle(data.points[0], data.radius, data.color, data.segments, data.lineWidth); - } - break; - case ShapeType::FilledCircle: - if (data.points.size() >= 1) { - drawFilledCircle(data.points[0], data.radius, data.color, data.segments); - } - break; - case ShapeType::Triangle: - if (data.points.size() >= 3) { - drawTriangle(data.points[0], data.points[1], data.points[2], - data.color, data.lineWidth); - } - break; - case ShapeType::FilledTriangle: - if (data.points.size() >= 3) { - drawFilledTriangle(data.points[0], data.points[1], data.points[2], data.color); - } - break; - case ShapeType::Polygon: - drawPolygon(data.points, data.color, data.lineWidth); - break; - case ShapeType::FilledPolygon: - drawFilledPolygon(data.points, data.color); - break; - } -} - -void ShapeBatcher::setViewProjection(const glm::mat4& viewProj) { - viewProj_ = viewProj; +void ShapeBatcher::setViewProjection(const glm::mat4 &viewProj) { + viewProj_ = viewProj; } void ShapeBatcher::resetStats() { - drawCallCount_ = 0; - shapeCount_ = 0; - batchCount_ = 0; + drawCallCount_ = 0; + shapeCount_ = 0; + batchCount_ = 0; } -void ShapeBatcher::setConfig(const ShapeBatcherConfig& config) { - config_ = config; +void ShapeBatcher::setConfig(const ShapeBatcherConfig &config) { + config_ = config; } -void ShapeBatcher::setShader(Ptr shader) { - shader_ = shader; -} +void ShapeBatcher::setShader(Ptr shader) { shader_ = shader; } bool ShapeBatcher::createShader() { - ShaderManager& shaderManager = ShaderManager::getInstance(); - - auto shaderHandle = shaderManager.getBuiltin("shape"); - if (!shaderHandle) { - return false; - } - - shader_ = shaderHandle->shader; - return shader_ != nullptr && shader_->isValid(); + ShaderManager &shaderManager = ShaderManager::getInstance(); + + auto shaderHandle = shaderManager.getBuiltin("shape"); + if (!shaderHandle) { + return false; + } + + shader_ = shaderHandle->shader; + return shader_ != nullptr && shader_->isValid(); } bool ShapeBatcher::createPipeline() { - if (!shader_) { - return false; - } + if (!shader_) { + return false; + } - rhi::PipelineBuilder builder; - builder.setShader(shader_) - .setBlendState(rhi::BlendState::alphaBlend()) - .setPrimitiveType(rhi::PrimitiveType::Triangles) - .addVertexAttribute(rhi::VertexAttribute(0, rhi::Format::RG32_FLOAT, 0, 0)) - .addVertexAttribute(rhi::VertexAttribute(1, rhi::Format::RGBA32_FLOAT, 8, 0)) - .addVertexBufferBinding(rhi::VertexBufferBinding(0, sizeof(ShapeVertex), 0)); + rhi::PipelineBuilder builder; + builder.setShader(shader_) + .setBlendState(rhi::BlendState::alphaBlend()) + .setPrimitiveType(rhi::PrimitiveType::Triangles) + .addVertexAttribute( + rhi::VertexAttribute(0, rhi::Format::RG32_FLOAT, 0, 0)) + .addVertexAttribute( + rhi::VertexAttribute(1, rhi::Format::RGBA32_FLOAT, 8, 0)) + .addVertexBufferBinding( + rhi::VertexBufferBinding(0, sizeof(ShapeVertex), 0)); - pipeline_ = device_->createPipeline(builder); - if (!pipeline_) { - return false; - } + pipeline_ = device_->createPipeline(builder); + if (!pipeline_) { + return false; + } - rhi::PipelineBuilder lineBuilder; - lineBuilder.setShader(shader_) - .setBlendState(rhi::BlendState::alphaBlend()) - .setPrimitiveType(rhi::PrimitiveType::Lines) - .addVertexAttribute(rhi::VertexAttribute(0, rhi::Format::RG32_FLOAT, 0, 0)) - .addVertexAttribute(rhi::VertexAttribute(1, rhi::Format::RGBA32_FLOAT, 8, 0)) - .addVertexBufferBinding(rhi::VertexBufferBinding(0, sizeof(ShapeVertex), 0)); + rhi::PipelineBuilder lineBuilder; + lineBuilder.setShader(shader_) + .setBlendState(rhi::BlendState::alphaBlend()) + .setPrimitiveType(rhi::PrimitiveType::Lines) + .addVertexAttribute( + rhi::VertexAttribute(0, rhi::Format::RG32_FLOAT, 0, 0)) + .addVertexAttribute( + rhi::VertexAttribute(1, rhi::Format::RGBA32_FLOAT, 8, 0)) + .addVertexBufferBinding( + rhi::VertexBufferBinding(0, sizeof(ShapeVertex), 0)); - linePipeline_ = device_->createPipeline(lineBuilder); - return linePipeline_ != nullptr; + linePipeline_ = device_->createPipeline(lineBuilder); + return linePipeline_ != nullptr; } bool ShapeBatcher::createBuffers() { - size_t vertexBufferSize = config_.maxBatchSize * 4 * sizeof(ShapeVertex); - size_t indexBufferSize = config_.maxBatchSize * 6 * sizeof(uint32_t); + size_t vertexBufferSize = config_.maxBatchSize * 4 * sizeof(ShapeVertex); + size_t indexBufferSize = config_.maxBatchSize * 6 * sizeof(uint32_t); - vertexBuffer_ = device_->createVertexBuffer(vertexBufferSize, rhi::BufferUsage::Dynamic); - if (!vertexBuffer_) { - return false; - } + vertexBuffer_ = + device_->createVertexBuffer(vertexBufferSize, rhi::BufferUsage::Dynamic); + if (!vertexBuffer_) { + return false; + } - indexBuffer_ = device_->createIndexBuffer(indexBufferSize, rhi::BufferUsage::Dynamic); - if (!indexBuffer_) { - return false; - } + indexBuffer_ = + device_->createIndexBuffer(indexBufferSize, rhi::BufferUsage::Dynamic); + if (!indexBuffer_) { + return false; + } - uniformBuffer_ = device_->createUniformBuffer(sizeof(glm::mat4)); - if (!uniformBuffer_) { - return false; - } + uniformBuffer_ = device_->createUniformBuffer(sizeof(glm::mat4)); + if (!uniformBuffer_) { + return false; + } - return true; + return true; } -void ShapeBatcher::flushBatch(rhi::PrimitiveType primitiveType, float lineWidth) { - if (vertices_.empty()) { - return; - } +void ShapeBatcher::flushBatch(rhi::PrimitiveType primitiveType, + float lineWidth) { + if (vertices_.empty()) { + return; + } - vertexBuffer_->setData(vertices_.data(), vertices_.size() * sizeof(ShapeVertex)); - uniformBuffer_->setValue(viewProj_); + vertexBuffer_->setData(vertices_.data(), + vertices_.size() * sizeof(ShapeVertex)); + uniformBuffer_->setValue(viewProj_); - Ptr activePipeline = (primitiveType == rhi::PrimitiveType::Lines) - ? linePipeline_ : pipeline_; + Ptr activePipeline = + (primitiveType == rhi::PrimitiveType::Lines) ? linePipeline_ : pipeline_; - device_->setPipeline(activePipeline); - device_->setShader(shader_); - device_->setVertexBuffer(0, vertexBuffer_); - device_->setUniformBuffer(0, uniformBuffer_); - device_->setUniformMat4("uViewProj", viewProj_); + device_->setPipeline(activePipeline); + device_->setShader(shader_); + device_->setVertexBuffer(0, vertexBuffer_); + device_->setUniformBuffer(0, uniformBuffer_); + device_->setUniformMat4("uViewProj", viewProj_); - if (!indices_.empty() && primitiveType == rhi::PrimitiveType::Triangles) { - indexBuffer_->setData(indices_.data(), indices_.size() * sizeof(uint32_t)); - device_->setIndexBuffer(indexBuffer_, rhi::IndexFormat::UInt32); - device_->drawIndexed(static_cast(indices_.size())); - } else { - device_->draw(static_cast(vertices_.size())); - } + if (!indices_.empty() && primitiveType == rhi::PrimitiveType::Triangles) { + indexBuffer_->setData(indices_.data(), indices_.size() * sizeof(uint32_t)); + device_->setIndexBuffer(indexBuffer_, rhi::IndexFormat::UInt32); + device_->drawIndexed(static_cast(indices_.size())); + } else { + device_->draw(static_cast(vertices_.size())); + } - drawCallCount_++; - batchCount_++; + drawCallCount_++; + batchCount_++; - vertices_.clear(); - indices_.clear(); - currentVertexIndex_ = 0; - currentIndexIndex_ = 0; + vertices_.clear(); + indices_.clear(); + currentVertexIndex_ = 0; + currentIndexIndex_ = 0; } -uint32_t ShapeBatcher::addVertex(const Vec2& position, const Color& color) { - vertices_.push_back(ShapeVertex(position, color)); - return currentVertexIndex_++; +uint32_t ShapeBatcher::addVertex(const Vec2 &position, const Color &color) { + vertices_.push_back(ShapeVertex(position, color)); + return currentVertexIndex_++; } void ShapeBatcher::addIndex(uint32_t index) { - indices_.push_back(index); - currentIndexIndex_++; + indices_.push_back(index); + currentIndexIndex_++; } -void ShapeBatcher::generateCircleVertices(const Vec2& center, float radius, - int segments, std::vector& vertices) const { - vertices.clear(); - vertices.reserve(segments); +void ShapeBatcher::generateCircleVertices(const Vec2 ¢er, float radius, + int segments, + std::vector &vertices) const { + vertices.clear(); + vertices.reserve(segments); - float angleStep = 2.0f * 3.14159265358979323846f / static_cast(segments); + float angleStep = + 2.0f * 3.14159265358979323846f / static_cast(segments); - for (int i = 0; i < segments; ++i) { - float angle = angleStep * static_cast(i); - float x = center.x + radius * std::cos(angle); - float y = center.y + radius * std::sin(angle); - vertices.push_back(Vec2(x, y)); - } + for (int i = 0; i < segments; ++i) { + float angle = angleStep * static_cast(i); + float x = center.x + radius * std::cos(angle); + float y = center.y + radius * std::sin(angle); + vertices.push_back(Vec2(x, y)); + } } -void ShapeBatcher::generateEllipseVertices(const Vec2& center, float radiusX, float radiusY, - int segments, std::vector& vertices) const { - vertices.clear(); - vertices.reserve(segments); +void ShapeBatcher::generateEllipseVertices(const Vec2 ¢er, float radiusX, + float radiusY, int segments, + std::vector &vertices) const { + vertices.clear(); + vertices.reserve(segments); - float angleStep = 2.0f * 3.14159265358979323846f / static_cast(segments); + float angleStep = + 2.0f * 3.14159265358979323846f / static_cast(segments); - for (int i = 0; i < segments; ++i) { - float angle = angleStep * static_cast(i); - float x = center.x + radiusX * std::cos(angle); - float y = center.y + radiusY * std::sin(angle); - vertices.push_back(Vec2(x, y)); - } + for (int i = 0; i < segments; ++i) { + float angle = angleStep * static_cast(i); + float x = center.x + radiusX * std::cos(angle); + float y = center.y + radiusY * std::sin(angle); + vertices.push_back(Vec2(x, y)); + } } } // namespace extra2d diff --git a/Extra2D/src/render/camera/camera.cpp b/Extra2D/src/render/camera/camera.cpp new file mode 100644 index 0000000..07c2247 --- /dev/null +++ b/Extra2D/src/render/camera/camera.cpp @@ -0,0 +1,203 @@ +#include + +#include +#include +#include + +namespace extra2d { + +Camera::Camera() + : position_(0.0f, 0.0f), rotation_(0.0f), zoom_(1.0f), left_(-640.0f), + right_(640.0f), bottom_(-360.0f), top_(360.0f), hasBounds_(false), + matricesDirty_(true), viewportAdapter_(nullptr) { + updateMatrices(); +} + +Camera::Camera(float left, float right, float bottom, float top) + : position_(0.0f, 0.0f), rotation_(0.0f), zoom_(1.0f), left_(left), + right_(right), bottom_(bottom), top_(top), hasBounds_(false), + matricesDirty_(true), viewportAdapter_(nullptr) { + updateMatrices(); +} + +void Camera::setViewport(float left, float right, float bottom, float top) { + left_ = left; + right_ = right; + bottom_ = bottom; + top_ = top; + matricesDirty_ = true; +} + +void Camera::setViewportSize(float width, float height) { + float halfWidth = width / 2.0f; + float halfHeight = height / 2.0f; + left_ = -halfWidth; + right_ = halfWidth; + bottom_ = -halfHeight; + top_ = halfHeight; + matricesDirty_ = true; +} + +Rect Camera::getViewport() const { + return Rect(left_, bottom_, right_ - left_, top_ - bottom_); +} + +void Camera::setPos(const Vec2 &position) { + position_ = position; + applyBounds(); + matricesDirty_ = true; +} + +void Camera::setPos(float x, float y) { setPos(Vec2(x, y)); } + +void Camera::move(const Vec2 &offset) { + position_ += offset; + applyBounds(); + matricesDirty_ = true; +} + +void Camera::move(float x, float y) { move(Vec2(x, y)); } + +void Camera::setRotation(float degrees) { + rotation_ = degrees; + matricesDirty_ = true; +} + +void Camera::rotate(float degrees) { + rotation_ += degrees; + matricesDirty_ = true; +} + +void Camera::setZoom(float zoom) { + zoom_ = std::max(0.001f, zoom); + matricesDirty_ = true; +} + +void Camera::zoomBy(float factor) { + zoom_ *= factor; + zoom_ = std::max(0.001f, zoom_); + matricesDirty_ = true; +} + +void Camera::setBounds(const Rect &bounds) { + bounds_ = bounds; + hasBounds_ = true; + applyBounds(); + matricesDirty_ = true; +} + +void Camera::clearBounds() { + hasBounds_ = false; + matricesDirty_ = true; +} + +glm::mat4 Camera::getViewMatrix() const { + if (matricesDirty_) { + const_cast(this)->updateMatrices(); + } + return viewMatrix_; +} + +glm::mat4 Camera::getProjectionMatrix() const { + if (matricesDirty_) { + const_cast(this)->updateMatrices(); + } + return projectionMatrix_; +} + +glm::mat4 Camera::getViewProjectionMatrix() const { + if (matricesDirty_) { + const_cast(this)->updateMatrices(); + } + return viewProjectionMatrix_; +} + +void Camera::updateMatrices() { + glm::mat4 transform = glm::mat4(1.0f); + transform = + glm::translate(transform, glm::vec3(position_.x, position_.y, 0.0f)); + transform = glm::rotate(transform, rotation_ * DEG_TO_RAD, + glm::vec3(0.0f, 0.0f, 1.0f)); + transform = glm::scale(transform, glm::vec3(zoom_, zoom_, 1.0f)); + + viewMatrix_ = glm::inverse(transform); + + projectionMatrix_ = glm::ortho(left_, right_, bottom_, top_, -1.0f, 1.0f); + + viewProjectionMatrix_ = projectionMatrix_ * viewMatrix_; + + matricesDirty_ = false; +} + +Vec2 Camera::screenToWorld(const Vec2 &screenPos) const { + if (matricesDirty_) { + const_cast(this)->updateMatrices(); + } + + glm::vec4 ndc(screenPos.x, screenPos.y, 0.0f, 1.0f); + glm::mat4 invVP = glm::inverse(viewProjectionMatrix_); + glm::vec4 world = invVP * ndc; + + return Vec2(world.x, world.y); +} + +Vec2 Camera::worldToScreen(const Vec2 &worldPos) const { + if (matricesDirty_) { + const_cast(this)->updateMatrices(); + } + + glm::vec4 world(worldPos.x, worldPos.y, 0.0f, 1.0f); + glm::vec4 screen = viewProjectionMatrix_ * world; + + return Vec2(screen.x, screen.y); +} + +void Camera::lookAt(const Vec2 &target) { + position_ = -target; + applyBounds(); + matricesDirty_ = true; +} + +void Camera::applyViewportAdapter() { + if (!viewportAdapter_) { + return; + } + + const ViewportResult &result = viewportAdapter_->getResult(); + float designWidth = viewportAdapter_->getDesignWidth(); + float designHeight = viewportAdapter_->getDesignHeight(); + + float halfWidth = designWidth / 2.0f; + float halfHeight = designHeight / 2.0f; + + setViewport(-halfWidth, halfWidth, -halfHeight, halfHeight); + setZoom(result.scale); +} + +void Camera::applyBounds() { + if (!hasBounds_) { + return; + } + + float viewWidth = (right_ - left_) / zoom_; + float viewHeight = (top_ - bottom_) / zoom_; + + float minX = bounds_.left() + viewWidth / 2.0f; + float maxX = bounds_.right() - viewWidth / 2.0f; + float minY = bounds_.top() + viewHeight / 2.0f; + float maxY = bounds_.bottom() - viewHeight / 2.0f; + + if (minX > maxX) { + position_.x = (bounds_.left() + bounds_.right()) / 2.0f; + } else { + position_.x = std::clamp(position_.x, minX, maxX); + } + + if (minY > maxY) { + position_.y = (bounds_.top() + bounds_.bottom()) / 2.0f; + } else { + position_.y = std::clamp(position_.y, minY, maxY); + } +} + +} // namespace extra2d diff --git a/Extra2D/src/render/camera/viewport_adapter.cpp b/Extra2D/src/render/camera/viewport_adapter.cpp new file mode 100644 index 0000000..683f514 --- /dev/null +++ b/Extra2D/src/render/camera/viewport_adapter.cpp @@ -0,0 +1,168 @@ +#include + +#include +#include + +namespace extra2d { + +ViewportAdapter::ViewportAdapter() : config_(ViewportConfig::hd()) {} + +ViewportAdapter::ViewportAdapter(const ViewportConfig& config) : config_(config) {} + +void ViewportAdapter::setConfig(const ViewportConfig& config) { + config_ = config; + calculateViewport(); +} + +void ViewportAdapter::setDesignSize(float width, float height) { + config_.designWidth = width; + config_.designHeight = height; + calculateViewport(); +} + +void ViewportAdapter::setScaleMode(ViewportScaleMode mode) { + config_.scaleMode = mode; + calculateViewport(); +} + +void ViewportAdapter::setAlign(ViewportAlign align) { + config_.align = align; + calculateViewport(); +} + +void ViewportAdapter::update(int screenWidth, int screenHeight) { + result_.screenWidth = screenWidth; + result_.screenHeight = screenHeight; + calculateViewport(); +} + +float ViewportAdapter::getDesignAspectRatio() const { + if (config_.designHeight <= 0.0f) { + return 1.0f; + } + return config_.designWidth / config_.designHeight; +} + +float ViewportAdapter::getScreenAspectRatio() const { + if (result_.screenHeight <= 0) { + return 1.0f; + } + return static_cast(result_.screenWidth) / static_cast(result_.screenHeight); +} + +Vec2 ViewportAdapter::screenToDesign(const Vec2& screenPos) const { + float x = (screenPos.x - static_cast(result_.offsetX)) / result_.scale; + float y = (screenPos.y - static_cast(result_.offsetY)) / result_.scale; + return Vec2(x, y); +} + +Vec2 ViewportAdapter::designToScreen(const Vec2& designPos) const { + float x = designPos.x * result_.scale + static_cast(result_.offsetX); + float y = designPos.y * result_.scale + static_cast(result_.offsetY); + return Vec2(x, y); +} + +void ViewportAdapter::calculateViewport() { + if (result_.screenWidth <= 0 || result_.screenHeight <= 0) { + return; + } + + result_.scale = calculateScale(); + + result_.width = config_.designWidth * result_.scale; + result_.height = config_.designHeight * result_.scale; + + calculateOffset(); +} + +float ViewportAdapter::calculateScale() const { + float scaleX = static_cast(result_.screenWidth) / config_.designWidth; + float scaleY = static_cast(result_.screenHeight) / config_.designHeight; + + float scale = 1.0f; + + switch (config_.scaleMode) { + case ViewportScaleMode::None: + scale = 1.0f; + break; + + case ViewportScaleMode::Letterbox: + scale = std::min(scaleX, scaleY); + break; + + case ViewportScaleMode::Crop: + scale = std::max(scaleX, scaleY); + break; + + case ViewportScaleMode::Stretch: + scale = 1.0f; + break; + + case ViewportScaleMode::IntegerScale: + scale = std::min(scaleX, scaleY); + scale = std::floor(scale); + if (scale < 1.0f) { + scale = 1.0f; + } + break; + } + + scale = std::clamp(scale, config_.minScale, config_.maxScale); + + return scale; +} + +void ViewportAdapter::calculateOffset() { + switch (config_.align) { + case ViewportAlign::TopLeft: + result_.offsetX = 0; + result_.offsetY = 0; + break; + + case ViewportAlign::TopCenter: + result_.offsetX = (result_.screenWidth - static_cast(result_.width)) / 2; + result_.offsetY = 0; + break; + + case ViewportAlign::TopRight: + result_.offsetX = result_.screenWidth - static_cast(result_.width); + result_.offsetY = 0; + break; + + case ViewportAlign::CenterLeft: + result_.offsetX = 0; + result_.offsetY = (result_.screenHeight - static_cast(result_.height)) / 2; + break; + + case ViewportAlign::Center: + default: + result_.offsetX = (result_.screenWidth - static_cast(result_.width)) / 2; + result_.offsetY = (result_.screenHeight - static_cast(result_.height)) / 2; + break; + + case ViewportAlign::CenterRight: + result_.offsetX = result_.screenWidth - static_cast(result_.width); + result_.offsetY = (result_.screenHeight - static_cast(result_.height)) / 2; + break; + + case ViewportAlign::BottomLeft: + result_.offsetX = 0; + result_.offsetY = result_.screenHeight - static_cast(result_.height); + break; + + case ViewportAlign::BottomCenter: + result_.offsetX = (result_.screenWidth - static_cast(result_.width)) / 2; + result_.offsetY = result_.screenHeight - static_cast(result_.height); + break; + + case ViewportAlign::BottomRight: + result_.offsetX = result_.screenWidth - static_cast(result_.width); + result_.offsetY = result_.screenHeight - static_cast(result_.height); + break; + } + + result_.x = static_cast(result_.offsetX); + result_.y = static_cast(result_.offsetY); +} + +} // namespace extra2d diff --git a/Extra2D/src/render/core/backend_factory.cpp b/Extra2D/src/render/core/backend_factory.cpp new file mode 100644 index 0000000..bb85034 --- /dev/null +++ b/Extra2D/src/render/core/backend_factory.cpp @@ -0,0 +1,262 @@ +#include +#include +#include + +namespace extra2d { + +std::unordered_map& BackendFactory::getRegistry() { + static std::unordered_map registry; + return registry; +} + +std::mutex& BackendFactory::getMutex() { + static std::mutex mutex; + return mutex; +} + +std::string& BackendFactory::getDefaultName() { + static std::string defaultName = "opengl"; + return defaultName; +} + +/** + * @brief 注册渲染后端 + */ +void BackendFactory::reg(const std::string& name, + BackendFn backend, + const std::vector& windowBackends, + rhi::GraphicsAPI api, + const std::string& description, + int priority) { + std::lock_guard lock(getMutex()); + + BackendInfo info; + info.name = name; + info.createFn = backend; + info.compatibleWindowBackends = windowBackends; + info.api = api; + info.description = description; + info.priority = priority; + + getRegistry()[name] = info; +} + +/** + * @brief 注销渲染后端 + */ +void BackendFactory::unreg(const std::string& name) { + std::lock_guard lock(getMutex()); + getRegistry().erase(name); +} + +/** + * @brief 创建渲染后端 + */ +UniquePtr BackendFactory::createBackend(const std::string& name) { + std::lock_guard lock(getMutex()); + + auto& registry = getRegistry(); + auto it = registry.find(name); + if (it != registry.end() && it->second.createFn) { + return it->second.createFn(); + } + + return nullptr; +} + +/** + * @brief 创建默认渲染后端 + */ +UniquePtr BackendFactory::createDefaultBackend() { + std::lock_guard lock(getMutex()); + + auto& registry = getRegistry(); + if (registry.empty()) { + return makeUnique(); + } + + std::string defaultName = getDefaultName(); + auto it = registry.find(defaultName); + if (it != registry.end() && it->second.createFn) { + return it->second.createFn(); + } + + std::vector sorted; + for (auto& pair : registry) { + sorted.push_back(&pair.second); + } + std::sort(sorted.begin(), sorted.end(), + [](const BackendInfo* a, const BackendInfo* b) { + return a->priority > b->priority; + }); + + if (!sorted.empty() && sorted.front()->createFn) { + return sorted.front()->createFn(); + } + + return makeUnique(); +} + +/** + * @brief 根据窗口后端创建兼容的渲染后端 + */ +UniquePtr BackendFactory::createBackendForWindow(const std::string& windowBackend) { + std::lock_guard lock(getMutex()); + + auto& registry = getRegistry(); + + std::vector compatible; + for (auto& pair : registry) { + const auto& windowBackends = pair.second.compatibleWindowBackends; + if (windowBackends.empty() || + std::find(windowBackends.begin(), windowBackends.end(), windowBackend) != windowBackends.end()) { + compatible.push_back(&pair.second); + } + } + + std::sort(compatible.begin(), compatible.end(), + [](const BackendInfo* a, const BackendInfo* b) { + return a->priority > b->priority; + }); + + if (!compatible.empty() && compatible.front()->createFn) { + return compatible.front()->createFn(); + } + + return makeUnique(); +} + +/** + * @brief 根据图形 API 创建后端 + */ +UniquePtr BackendFactory::createBackendForAPI(rhi::GraphicsAPI api) { + std::lock_guard lock(getMutex()); + + auto& registry = getRegistry(); + + std::vector matching; + for (auto& pair : registry) { + if (pair.second.api == api) { + matching.push_back(&pair.second); + } + } + + std::sort(matching.begin(), matching.end(), + [](const BackendInfo* a, const BackendInfo* b) { + return a->priority > b->priority; + }); + + if (!matching.empty() && matching.front()->createFn) { + return matching.front()->createFn(); + } + + if (api == rhi::GraphicsAPI::OpenGL) { + return makeUnique(); + } + + return nullptr; +} + +/** + * @brief 检查后端是否已注册 + */ +bool BackendFactory::hasBackend(const std::string& name) { + std::lock_guard lock(getMutex()); + return getRegistry().find(name) != getRegistry().end(); +} + +/** + * @brief 获取所有已注册的后端名称 + */ +std::vector BackendFactory::getBackendNames() { + std::lock_guard lock(getMutex()); + + std::vector names; + for (const auto& pair : getRegistry()) { + names.push_back(pair.first); + } + return names; +} + +/** + * @brief 获取后端信息 + */ +const BackendFactory::BackendInfo* BackendFactory::getBackendInfo(const std::string& name) { + std::lock_guard lock(getMutex()); + + auto& registry = getRegistry(); + auto it = registry.find(name); + if (it != registry.end()) { + return &it->second; + } + return nullptr; +} + +/** + * @brief 获取所有后端信息 + */ +std::vector BackendFactory::getAllBackendInfos() { + std::lock_guard lock(getMutex()); + + std::vector infos; + for (const auto& pair : getRegistry()) { + infos.push_back(pair.second); + } + return infos; +} + +/** + * @brief 获取兼容指定窗口后端的所有渲染后端 + */ +std::vector BackendFactory::getCompatibleBackends(const std::string& windowBackend) { + std::lock_guard lock(getMutex()); + + std::vector names; + for (const auto& pair : getRegistry()) { + const auto& windowBackends = pair.second.compatibleWindowBackends; + if (windowBackends.empty() || + std::find(windowBackends.begin(), windowBackends.end(), windowBackend) != windowBackends.end()) { + names.push_back(pair.first); + } + } + return names; +} + +/** + * @brief 清除所有注册的后端 + */ +void BackendFactory::clear() { + std::lock_guard lock(getMutex()); + getRegistry().clear(); +} + +/** + * @brief 获取默认后端名称 + */ +std::string BackendFactory::getDefaultBackendName() { + return getDefaultName(); +} + +/** + * @brief 设置默认后端名称 + */ +void BackendFactory::setDefaultBackendName(const std::string& name) { + getDefaultName() = name; +} + +namespace { + +static BackendRegistrar s_openglBackendReg( + "opengl", + []() -> UniquePtr { + return makeUnique(); + }, + {"sdl2", "glfw"}, + rhi::GraphicsAPI::OpenGL, + "OpenGL 4.5 Core Profile", + 100 +); + +} + +} // namespace extra2d diff --git a/Extra2D/src/render/core/render_context.cpp b/Extra2D/src/render/core/render_context.cpp index ca4bdb2..bbe3f01 100644 --- a/Extra2D/src/render/core/render_context.cpp +++ b/Extra2D/src/render/core/render_context.cpp @@ -245,7 +245,7 @@ void RenderContext::flushQueue() { const RenderQueueStats& queueStats = queue_.getStats(); stats_.spriteCount = queueStats.spriteCommands; - stats_.textCount = queueStats.textCommands; + stats_.textCharCount = queueStats.textCommands; stats_.shapeCount = queueStats.shapeCommands; stats_.batchCount = queueStats.batchCount; diff --git a/Extra2D/src/render/render_module.cpp b/Extra2D/src/render/render_module.cpp new file mode 100644 index 0000000..75c0716 --- /dev/null +++ b/Extra2D/src/render/render_module.cpp @@ -0,0 +1,81 @@ +#include +#include +#include + +#include +#include +#include + +namespace extra2d { + +RenderModule::RenderModule() : initialized_(false) {} + +RenderModule::RenderModule(const RenderModuleConfig &config) + : config_(config), initialized_(false) {} + +RenderModule::~RenderModule() { shutdown(); } + +bool RenderModule::init() { + if (initialized_) { + return true; + } + + if (!app_) { + return false; + } + + IWindow *window = nullptr; + + renderer_ = Renderer::create(); + + if (!renderer_) { + return false; + } + + RendererConfig rendererConfig; + rendererConfig.api = config_.api; + rendererConfig.vsync = config_.vsync; + rendererConfig.maxBatchSize = config_.maxBatchSize; + rendererConfig.enableDebug = config_.enableDebug; + rendererConfig.viewportConfig = + ViewportConfig(config_.designWidth, config_.designHeight); + + renderer_->setConfig(rendererConfig); + + initialized_ = true; + return true; +} + +void RenderModule::shutdown() { + if (!initialized_) { + return; + } + + renderer_.reset(); + initialized_ = false; +} + +std::vector RenderModule::deps() const { + return {std::type_index(typeid(class WindowModule))}; +} + +void RenderModule::setConfig(const RenderModuleConfig &config) { + config_ = config; + + if (renderer_ && initialized_) { + RendererConfig rendererConfig; + rendererConfig.api = config_.api; + rendererConfig.vsync = config_.vsync; + rendererConfig.maxBatchSize = config_.maxBatchSize; + rendererConfig.enableDebug = config_.enableDebug; + rendererConfig.viewportConfig = + ViewportConfig(config_.designWidth, config_.designHeight); + renderer_->setConfig(rendererConfig); + } +} + +Ptr RenderModule::create(const RenderModuleConfig &config) { + return makePtr(config); +} + +} // namespace extra2d diff --git a/Extra2D/src/render/renderer.cpp b/Extra2D/src/render/renderer.cpp new file mode 100644 index 0000000..388dbe4 --- /dev/null +++ b/Extra2D/src/render/renderer.cpp @@ -0,0 +1,349 @@ +#include + +#include +#include +#include +#include + +namespace extra2d { + +Renderer::Renderer() + : window_(nullptr), width_(0), height_(0), initialized_(false) {} + +Renderer::Renderer(const RendererConfig &config) + : window_(nullptr), config_(config), width_(0), height_(0), + initialized_(false) {} + +Renderer::~Renderer() { shutdown(); } + +bool Renderer::init() { return initialized_; } + +bool Renderer::init(IWindow *window) { return init(window, config_); } + +bool Renderer::init(IWindow *window, const RendererConfig &config) { + if (initialized_) { + return true; + } + + if (!window) { + return false; + } + + window_ = window; + config_ = config; + width_ = window->width(); + height_ = window->height(); + + viewportAdapter_.setConfig(config.viewportConfig); + viewportAdapter_.update(width_, height_); + + RenderBackendConfig backendConfig; + backendConfig.api = config.api; + backendConfig.vsync = config.vsync; + backendConfig.maxBatchSize = config.maxBatchSize; + backendConfig.enableDebug = config.enableDebug; + + backend_ = BackendFactory::createBackendForAPI(config.api); + if (!backend_ || !backend_->init(window, backendConfig)) { + return false; + } + + RenderContextConfig contextConfig; + contextConfig.maxBatchSize = config.maxBatchSize; + contextConfig.preferredAPI = config.api; + + context_ = makeUnique(contextConfig); + if (!context_->init(window, config.api)) { + return false; + } + + if (!createBatchers()) { + return false; + } + + createDefaultCamera(); + + initialized_ = true; + return true; +} + +void Renderer::shutdown() { + if (!initialized_) { + return; + } + + textBatcher_.reset(); + shapeBatcher_.reset(); + spriteBatcher_.reset(); + context_.reset(); + backend_.reset(); + + activeCamera_.reset(); + defaultCamera_.reset(); + + window_ = nullptr; + width_ = 0; + height_ = 0; + initialized_ = false; +} + +void Renderer::beginFrame(const Color &clearColor) { + if (!initialized_) { + return; + } + + if (backend_) { + backend_->beginFrame(clearColor); + } + + if (context_) { + context_->beginFrame(); + context_->clear(clearColor); + } +} + +void Renderer::endFrame() { + if (!initialized_) { + return; + } + + if (spriteBatcher_) { + spriteBatcher_->end(); + } + if (shapeBatcher_) { + shapeBatcher_->end(); + } + if (textBatcher_) { + textBatcher_->end(); + } + + if (context_) { + context_->endFrame(); + } + + if (backend_) { + backend_->endFrame(); + } +} + +void Renderer::present() { + if (!initialized_) { + return; + } + + if (backend_) { + backend_->present(); + } + + if (context_) { + context_->present(); + } +} + +void Renderer::renderScene(Scene *scene) { + if (!initialized_ || !scene) { + return; + } + + Camera *camera = activeCamera_ ? activeCamera_.get() : defaultCamera_.get(); + if (!camera) { + camera = scene->getActiveCamera(); + } + + renderScene(scene, camera); +} + +void Renderer::renderScene(Scene *scene, Camera *camera) { + if (!initialized_ || !scene) { + return; + } + + if (!camera) { + camera = defaultCamera_.get(); + } + + if (camera) { + camera->setViewportAdapter(&viewportAdapter_); + camera->applyViewportAdapter(); + } + + applyCameraTransform(camera); + + scene->renderScene(*backend_); +} + +void Renderer::setActiveCamera(Ptr camera) { activeCamera_ = camera; } + +Ptr Renderer::getActiveCamera() const { + return activeCamera_ ? activeCamera_ : defaultCamera_; +} + +void Renderer::setViewport(int x, int y, int width, int height) { + if (context_) { + context_->setViewport(x, y, width, height); + } + if (backend_) { + backend_->setViewport(x, y, width, height); + } +} + +void Renderer::setScissor(int x, int y, int width, int height) { + if (context_) { + context_->setScissorRect(x, y, width, height); + } + if (backend_) { + backend_->setScissor(x, y, width, height); + } +} + +void Renderer::setScissorEnabled(bool enabled) { + if (context_) { + context_->setScissorEnabled(enabled); + } + if (backend_) { + backend_->setScissorEnabled(enabled); + } +} + +void Renderer::setVSync(bool enabled) { + if (context_) { + context_->setVSync(enabled); + } + if (backend_) { + backend_->setVSync(enabled); + } +} + +bool Renderer::isVSyncEnabled() const { + if (backend_) { + return backend_->isVSyncEnabled(); + } + if (context_) { + return context_->isVSyncEnabled(); + } + return false; +} + +rhi::RHIDevice *Renderer::getDevice() const { + if (backend_) { + return backend_->getDevice(); + } + if (context_) { + return context_->getDevice().get(); + } + return nullptr; +} + +const RenderStats &Renderer::getStats() const { + if (backend_) { + return backend_->getStats(); + } + if (context_) { + return context_->getStats(); + } + static RenderStats empty; + return empty; +} + +void Renderer::resetStats() { + if (backend_) { + backend_->resetStats(); + } + if (context_) { + context_->resetStats(); + } + if (spriteBatcher_) { + spriteBatcher_->resetStats(); + } + if (shapeBatcher_) { + shapeBatcher_->resetStats(); + } + if (textBatcher_) { + textBatcher_->resetStats(); + } +} + +void Renderer::setConfig(const RendererConfig &config) { + config_ = config; + viewportAdapter_.setConfig(config.viewportConfig); + updateViewportAdapter(); +} + +bool Renderer::createBatchers() { + auto device = getDevice(); + if (!device) { + return false; + } + + auto devicePtr = context_ ? context_->getDevice() : nullptr; + if (!devicePtr) { + return false; + } + + SpriteBatcherConfig spriteConfig; + spriteConfig.maxBatchSize = config_.maxBatchSize; + spriteBatcher_ = makeUnique(spriteConfig); + if (!spriteBatcher_->init(devicePtr)) { + return false; + } + + ShapeBatcherConfig shapeConfig; + shapeConfig.maxBatchSize = config_.maxBatchSize / 2; + shapeBatcher_ = makeUnique(shapeConfig); + if (!shapeBatcher_->init(devicePtr)) { + return false; + } + + TextBatcherConfig textConfig; + textConfig.maxBatchSize = config_.maxBatchSize / 2; + textBatcher_ = makeUnique(textConfig); + if (!textBatcher_->init(devicePtr)) { + return false; + } + + return true; +} + +void Renderer::createDefaultCamera() { + float designWidth = viewportAdapter_.getDesignWidth(); + float designHeight = viewportAdapter_.getDesignHeight(); + + defaultCamera_ = makePtr(); + defaultCamera_->setViewport(0, designWidth, 0, designHeight); + defaultCamera_->setViewportAdapter(&viewportAdapter_); + defaultCamera_->setDebugName("DefaultCamera"); +} + +void Renderer::updateViewportAdapter() { + viewportAdapter_.update(width_, height_); +} + +void Renderer::applyCameraTransform(Camera *camera) { + if (!camera) { + return; + } + + camera->updateMatrices(); + glm::mat4 viewProj = camera->getViewProjectionMatrix(); + + if (spriteBatcher_) { + spriteBatcher_->setViewProjection(viewProj); + } + if (shapeBatcher_) { + shapeBatcher_->setViewProjection(viewProj); + } + if (textBatcher_) { + textBatcher_->setViewProjection(viewProj); + } + + if (backend_) { + backend_->setProjectionMatrix(camera->getProjectionMatrix()); + backend_->setViewMatrix(camera->getViewMatrix()); + } +} + +Ptr Renderer::create(const RendererConfig &config) { + return makePtr(config); +} + +} // namespace extra2d diff --git a/Extra2D/src/scene/node.cpp b/Extra2D/src/scene/node.cpp index 446175e..a0af6a9 100644 --- a/Extra2D/src/scene/node.cpp +++ b/Extra2D/src/scene/node.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -13,10 +12,10 @@ namespace extra2d { * @param name 节点名称 * @return 创建的节点智能指针 */ -Ptr Node::create(const std::string& name) { - auto node = makePtr(); - node->setName(name); - return node; +Ptr Node::create(const std::string &name) { + auto node = makePtr(); + node->setName(name); + return node; } /** @@ -25,11 +24,11 @@ Ptr Node::create(const std::string& name) { * 创建一个空的节点对象,并自动添加变换组件 */ Node::Node() { - // 自动添加变换组件 - auto transform = makePtr(); - transform->setNode(this); - transform_ = transform.get(); - components_[TransformComponent::getStaticTypeId()] = transform; + // 自动添加变换组件 + auto transform = makePtr(); + transform->setNode(this); + transform_ = transform.get(); + components_[TransformComponent::getStaticTypeId()] = transform; } /** @@ -38,14 +37,14 @@ Node::Node() { * 清除所有子节点和组件 */ Node::~Node() { - clearChildren(); - - // 销毁所有组件 - for (auto& pair : components_) { - pair.second->destroy(); - pair.second->setNode(nullptr); - } - components_.clear(); + clearChildren(); + + // 销毁所有组件 + for (auto &pair : components_) { + pair.second->destroy(); + pair.second->setNode(nullptr); + } + components_.clear(); } // ------------------------------------------------------------------------- @@ -57,68 +56,68 @@ Node::~Node() { * @param child 要添加的子节点智能指针 */ void Node::addChild(Ptr child) { - if (!child || child.get() == this) { - return; - } + if (!child || child.get() == this) { + return; + } - child->detach(); - child->parent_ = weak_from_this(); - children_.push_back(child); - childrenOrderDirty_ = true; + child->detach(); + child->parent_ = weak_from_this(); + children_.push_back(child); + childrenOrderDirty_ = true; - // 更新索引 - if (!child->getName().empty()) { - nameIndex_[child->getName()] = child; - } - if (child->getTag() != -1) { - tagIndex_[child->getTag()] = child; - } + // 更新索引 + if (!child->getName().empty()) { + nameIndex_[child->getName()] = child; + } + if (child->getTag() != -1) { + tagIndex_[child->getTag()] = child; + } - if (running_) { - child->onEnter(); - if (scene_) { - child->onAttachToScene(scene_); - } + if (running_) { + child->onEnter(); + if (scene_) { + child->onAttachToScene(scene_); } + } } /** * @brief 批量添加子节点 * @param children 要添加的子节点数组(右值引用) */ -void Node::addChildren(std::vector>&& children) { - size_t newSize = children_.size() + children.size(); - if (newSize > children_.capacity()) { - children_.reserve(newSize); +void Node::addChildren(std::vector> &&children) { + size_t newSize = children_.size() + children.size(); + if (newSize > children_.capacity()) { + children_.reserve(newSize); + } + + for (auto &child : children) { + if (!child || child.get() == this) { + continue; } - for (auto& child : children) { - if (!child || child.get() == this) { - continue; - } + child->detach(); + child->parent_ = weak_from_this(); + children_.push_back(child); - child->detach(); - child->parent_ = weak_from_this(); - children_.push_back(child); - - if (!child->getName().empty()) { - nameIndex_[child->getName()] = child; - } - if (child->getTag() != -1) { - tagIndex_[child->getTag()] = child; - } - - if (running_) { - child->onEnter(); - if (scene_) { - child->onAttachToScene(scene_); - } - } + if (!child->getName().empty()) { + nameIndex_[child->getName()] = child; + } + if (child->getTag() != -1) { + tagIndex_[child->getTag()] = child; } - if (!children.empty()) { - childrenOrderDirty_ = true; + if (running_) { + child->onEnter(); + if (scene_) { + child->onAttachToScene(scene_); + } } + } + + if (!children.empty()) { + childrenOrderDirty_ = true; + } } /** @@ -126,69 +125,69 @@ void Node::addChildren(std::vector>&& children) { * @param child 要移除的子节点智能指针 */ void Node::removeChild(Ptr child) { - if (!child) - return; + if (!child) + return; - auto it = std::find(children_.begin(), children_.end(), child); - if (it != children_.end()) { - (*it)->onDetachFromScene(); + auto it = std::find(children_.begin(), children_.end(), child); + if (it != children_.end()) { + (*it)->onDetachFromScene(); - if (running_) { - (*it)->onExit(); - } - if (!(*it)->getName().empty()) { - nameIndex_.erase((*it)->getName()); - } - if ((*it)->getTag() != -1) { - tagIndex_.erase((*it)->getTag()); - } - (*it)->parent_.reset(); - children_.erase(it); + if (running_) { + (*it)->onExit(); } + if (!(*it)->getName().empty()) { + nameIndex_.erase((*it)->getName()); + } + if ((*it)->getTag() != -1) { + tagIndex_.erase((*it)->getTag()); + } + (*it)->parent_.reset(); + children_.erase(it); + } } /** * @brief 通过名称移除子节点 * @param name 子节点的名称 */ -void Node::removeChildByName(const std::string& name) { - auto child = findChild(name); - if (child) { - removeChild(child); - } +void Node::removeChildByName(const std::string &name) { + auto child = findChild(name); + if (child) { + removeChild(child); + } } /** * @brief 从父节点分离 */ void Node::detach() { - auto p = parent_.lock(); - if (p) { - Ptr self; - try { - self = shared_from_this(); - } catch (const std::bad_weak_ptr&) { - parent_.reset(); - return; - } - p->removeChild(self); + auto p = parent_.lock(); + if (p) { + Ptr self; + try { + self = shared_from_this(); + } catch (const std::bad_weak_ptr &) { + parent_.reset(); + return; } + p->removeChild(self); + } } /** * @brief 清除所有子节点 */ void Node::clearChildren() { - for (auto& child : children_) { - if (running_) { - child->onDetachFromScene(); - child->onExit(); - } - child->parent_.reset(); + for (auto &child : children_) { + if (running_) { + child->onDetachFromScene(); + child->onExit(); } - children_.clear(); - nameIndex_.clear(); - tagIndex_.clear(); + child->parent_.reset(); + } + children_.clear(); + nameIndex_.clear(); + tagIndex_.clear(); } /** @@ -196,12 +195,12 @@ void Node::clearChildren() { * @param name 子节点的名称 * @return 找到的子节点智能指针,未找到返回nullptr */ -Ptr Node::findChild(const std::string& name) const { - auto it = nameIndex_.find(name); - if (it != nameIndex_.end()) { - return it->second.lock(); - } - return nullptr; +Ptr Node::findChild(const std::string &name) const { + auto it = nameIndex_.find(name); + if (it != nameIndex_.end()) { + return it->second.lock(); + } + return nullptr; } /** @@ -210,11 +209,11 @@ Ptr Node::findChild(const std::string& name) const { * @return 找到的子节点智能指针,未找到返回nullptr */ Ptr Node::findChildByTag(int tag) const { - auto it = tagIndex_.find(tag); - if (it != tagIndex_.end()) { - return it->second.lock(); - } - return nullptr; + auto it = tagIndex_.find(tag); + if (it != tagIndex_.end()) { + return it->second.lock(); + } + return nullptr; } // ------------------------------------------------------------------------- @@ -225,11 +224,11 @@ Ptr Node::findChildByTag(int tag) const { * @brief 设置节点位置 * @param pos 新的位置坐标 */ -void Node::setPos(const Vec2& pos) { - if (transform_) { - transform_->setPosition(pos); - } - markTransformDirty(); +void Node::setPos(const Vec2 &pos) { + if (transform_) { + transform_->setPosition(pos); + } + markTransformDirty(); } /** @@ -237,19 +236,17 @@ void Node::setPos(const Vec2& pos) { * @param x X坐标 * @param y Y坐标 */ -void Node::setPos(float x, float y) { - setPos(Vec2(x, y)); -} +void Node::setPos(float x, float y) { setPos(Vec2(x, y)); } /** * @brief 获取节点位置 * @return 位置坐标 */ Vec2 Node::getPosition() const { - if (transform_) { - return transform_->getPosition(); - } - return Vec2::Zero(); + if (transform_) { + return transform_->getPosition(); + } + return Vec2::Zero(); } /** @@ -257,10 +254,10 @@ Vec2 Node::getPosition() const { * @param degrees 旋转角度(度数) */ void Node::setRotation(float degrees) { - if (transform_) { - transform_->setRotation(degrees); - } - markTransformDirty(); + if (transform_) { + transform_->setRotation(degrees); + } + markTransformDirty(); } /** @@ -268,60 +265,56 @@ void Node::setRotation(float degrees) { * @return 旋转角度(度数) */ float Node::getRotation() const { - if (transform_) { - return transform_->getRotation(); - } - return 0.0f; + if (transform_) { + return transform_->getRotation(); + } + return 0.0f; } /** * @brief 设置节点缩放 * @param scale 缩放向量 */ -void Node::setScale(const Vec2& scale) { - if (transform_) { - transform_->setScale(scale); - } - markTransformDirty(); +void Node::setScale(const Vec2 &scale) { + if (transform_) { + transform_->setScale(scale); + } + markTransformDirty(); } /** * @brief 设置节点统一缩放 * @param scale 统一缩放值 */ -void Node::setScale(float scale) { - setScale(Vec2(scale, scale)); -} +void Node::setScale(float scale) { setScale(Vec2(scale, scale)); } /** * @brief 设置节点缩放 * @param x X轴缩放值 * @param y Y轴缩放值 */ -void Node::setScale(float x, float y) { - setScale(Vec2(x, y)); -} +void Node::setScale(float x, float y) { setScale(Vec2(x, y)); } /** * @brief 获取节点缩放 * @return 缩放向量 */ Vec2 Node::getScale() const { - if (transform_) { - return transform_->getScale(); - } - return Vec2::One(); + if (transform_) { + return transform_->getScale(); + } + return Vec2::One(); } /** * @brief 设置节点锚点 * @param anchor 锚点位置(0-1范围) */ -void Node::setAnchor(const Vec2& anchor) { - if (transform_) { - transform_->setAnchor(anchor); - } - markTransformDirty(); +void Node::setAnchor(const Vec2 &anchor) { + if (transform_) { + transform_->setAnchor(anchor); + } + markTransformDirty(); } /** @@ -329,30 +322,28 @@ void Node::setAnchor(const Vec2& anchor) { * @param x 锚点X坐标(0-1范围) * @param y 锚点Y坐标(0-1范围) */ -void Node::setAnchor(float x, float y) { - setAnchor(Vec2(x, y)); -} +void Node::setAnchor(float x, float y) { setAnchor(Vec2(x, y)); } /** * @brief 获取节点锚点 * @return 锚点位置 */ Vec2 Node::getAnchor() const { - if (transform_) { - return transform_->getAnchor(); - } - return Vec2(0.5f, 0.5f); + if (transform_) { + return transform_->getAnchor(); + } + return Vec2(0.5f, 0.5f); } /** * @brief 设置节点斜切 * @param skew 斜切角度向量 */ -void Node::setSkew(const Vec2& skew) { - if (transform_) { - transform_->setSkew(skew); - } - markTransformDirty(); +void Node::setSkew(const Vec2 &skew) { + if (transform_) { + transform_->setSkew(skew); + } + markTransformDirty(); } /** @@ -360,19 +351,17 @@ void Node::setSkew(const Vec2& skew) { * @param x X轴斜切角度 * @param y Y轴斜切角度 */ -void Node::setSkew(float x, float y) { - setSkew(Vec2(x, y)); -} +void Node::setSkew(float x, float y) { setSkew(Vec2(x, y)); } /** * @brief 获取节点斜切 * @return 斜切角度向量 */ Vec2 Node::getSkew() const { - if (transform_) { - return transform_->getSkew(); - } - return Vec2::Zero(); + if (transform_) { + return transform_->getSkew(); + } + return Vec2::Zero(); } // ------------------------------------------------------------------------- @@ -384,11 +373,11 @@ Vec2 Node::getSkew() const { * @param localPos 本地坐标位置 * @return 世界坐标位置 */ -Vec2 Node::toWorld(const Vec2& localPos) const { - if (transform_) { - return transform_->localToWorld(localPos); - } - return localPos; +Vec2 Node::toWorld(const Vec2 &localPos) const { + if (transform_) { + return transform_->localToWorld(localPos); + } + return localPos; } /** @@ -396,11 +385,11 @@ Vec2 Node::toWorld(const Vec2& localPos) const { * @param worldPos 世界坐标位置 * @return 本地坐标位置 */ -Vec2 Node::toLocal(const Vec2& worldPos) const { - if (transform_) { - return transform_->worldToLocal(worldPos); - } - return worldPos; +Vec2 Node::toLocal(const Vec2 &worldPos) const { + if (transform_) { + return transform_->worldToLocal(worldPos); + } + return worldPos; } /** @@ -408,10 +397,10 @@ Vec2 Node::toLocal(const Vec2& worldPos) const { * @return 本地变换矩阵 */ glm::mat4 Node::getLocalTransform() const { - if (transform_) { - return transform_->getLocalTransform(); - } - return glm::mat4(1.0f); + if (transform_) { + return transform_->getLocalTransform(); + } + return glm::mat4(1.0f); } /** @@ -419,62 +408,62 @@ glm::mat4 Node::getLocalTransform() const { * @return 世界变换矩阵 */ glm::mat4 Node::getWorldTransform() const { - if (transform_) { - return transform_->getWorldTransform(); - } - return glm::mat4(1.0f); + if (transform_) { + return transform_->getWorldTransform(); + } + return glm::mat4(1.0f); } /** * @brief 标记变换矩阵为脏状态 */ void Node::markTransformDirty() { - if (!transformDirty_ || !worldTransformDirty_) { - transformDirty_ = true; - worldTransformDirty_ = true; + if (!transformDirty_ || !worldTransformDirty_) { + transformDirty_ = true; + worldTransformDirty_ = true; - // 递归标记所有子节点 - for (auto& child : children_) { - child->markTransformDirty(); - } + // 递归标记所有子节点 + for (auto &child : children_) { + child->markTransformDirty(); } + } } /** * @brief 批量更新变换矩阵 */ void Node::batchTransforms() { - if (transform_) { - transform_->batchTransforms(); - } - - transformDirty_ = false; - worldTransformDirty_ = false; + if (transform_) { + transform_->batchTransforms(); + } - // 递归更新子节点 - for (auto& child : children_) { - child->batchTransforms(); - } + transformDirty_ = false; + worldTransformDirty_ = false; + + // 递归更新子节点 + for (auto &child : children_) { + child->batchTransforms(); + } } /** * @brief 获取变换脏标记状态 */ bool Node::isTransformDirty() const { - if (transform_) { - return transform_->isLocalTransformDirty(); - } - return transformDirty_; + if (transform_) { + return transform_->isLocalTransformDirty(); + } + return transformDirty_; } /** * @brief 获取世界变换脏标记状态 */ bool Node::isWorldTransformDirty() const { - if (transform_) { - return transform_->isWorldTransformDirty(); - } - return worldTransformDirty_; + if (transform_) { + return transform_->isWorldTransformDirty(); + } + return worldTransformDirty_; } // ------------------------------------------------------------------------- @@ -486,50 +475,42 @@ bool Node::isWorldTransformDirty() const { * @param opacity 透明度值(0.0-1.0范围) */ void Node::setOpacity(float opacity) { - opacity_ = std::clamp(opacity, 0.0f, 1.0f); + opacity_ = std::clamp(opacity, 0.0f, 1.0f); } /** * @brief 设置可见性 * @param visible 是否可见 */ -void Node::setVisible(bool visible) { - visible_ = visible; -} +void Node::setVisible(bool visible) { visible_ = visible; } /** * @brief 设置颜色 * @param color RGB颜色值 */ -void Node::setColor(const Color3B& color) { - color_ = color; -} +void Node::setColor(const Color3B &color) { color_ = color; } /** * @brief 设置水平翻转 * @param flipX 是否水平翻转 */ -void Node::setFlipX(bool flipX) { - flipX_ = flipX; -} +void Node::setFlipX(bool flipX) { flipX_ = flipX; } /** * @brief 设置垂直翻转 * @param flipY 是否垂直翻转 */ -void Node::setFlipY(bool flipY) { - flipY_ = flipY; -} +void Node::setFlipY(bool flipY) { flipY_ = flipY; } /** * @brief 设置Z序 * @param zOrder 渲染层级顺序 */ void Node::setZOrder(int zOrder) { - if (zOrder_ != zOrder) { - zOrder_ = zOrder; - childrenOrderDirty_ = true; - } + if (zOrder_ != zOrder) { + zOrder_ = zOrder; + childrenOrderDirty_ = true; + } } // ------------------------------------------------------------------------- @@ -540,29 +521,29 @@ void Node::setZOrder(int zOrder) { * @brief 节点进入时的回调 */ void Node::onEnter() { - running_ = true; - - // 初始化所有组件 - for (auto& pair : components_) { - if (!pair.second->isInitialized()) { - pair.second->init(); - pair.second->setInitialized(true); - } - } - - for (auto& child : children_) { - child->onEnter(); + running_ = true; + + // 初始化所有组件 + for (auto &pair : components_) { + if (!pair.second->isInitialized()) { + pair.second->init(); + pair.second->setInitialized(true); } + } + + for (auto &child : children_) { + child->onEnter(); + } } /** * @brief 节点退出时的回调 */ void Node::onExit() { - running_ = false; - for (auto& child : children_) { - child->onExit(); - } + running_ = false; + for (auto &child : children_) { + child->onExit(); + } } /** @@ -570,60 +551,56 @@ void Node::onExit() { * @param dt 帧间隔时间(秒) */ void Node::onUpdate(float dt) { - onUpdateNode(dt); + onUpdateNode(dt); - // 更新所有启用的组件 - for (auto& pair : components_) { - if (pair.second->isEnabled()) { - pair.second->update(dt); - } + // 更新所有启用的组件 + for (auto &pair : components_) { + if (pair.second->isEnabled()) { + pair.second->update(dt); } + } - // 更新子节点 - for (auto& child : children_) { - child->onUpdate(dt); - } + // 更新子节点 + for (auto &child : children_) { + child->onUpdate(dt); + } } /** * @brief 渲染回调 * @param renderer 渲染后端引用 */ -void Node::onRender(RenderBackend& renderer) { - if (!visible_) - return; +void Node::onRender(RenderBackend &renderer) { + if (!visible_) + return; - renderer.pushTransform(getLocalTransform()); + onDraw(renderer); - onDraw(renderer); - - for (auto& child : children_) { - child->onRender(renderer); - } - - renderer.popTransform(); + for (auto &child : children_) { + child->onRender(renderer); + } } /** * @brief 附加到场景时的回调 * @param scene 所属场景指针 */ -void Node::onAttachToScene(Scene* scene) { - scene_ = scene; +void Node::onAttachToScene(Scene *scene) { + scene_ = scene; - for (auto& child : children_) { - child->onAttachToScene(scene); - } + for (auto &child : children_) { + child->onAttachToScene(scene); + } } /** * @brief 从场景分离时的回调 */ void Node::onDetachFromScene() { - scene_ = nullptr; - for (auto& child : children_) { - child->onDetachFromScene(); - } + scene_ = nullptr; + for (auto &child : children_) { + child->onDetachFromScene(); + } } /** @@ -631,59 +608,57 @@ void Node::onDetachFromScene() { * @return 节点的边界矩形 */ Rect Node::getBounds() const { - Vec2 pos = getPosition(); - return Rect(pos.x, pos.y, 0, 0); + Vec2 pos = getPosition(); + return Rect(pos.x, pos.y, 0, 0); } /** * @brief 更新节点 * @param dt 帧间隔时间(秒) */ -void Node::update(float dt) { - onUpdate(dt); -} +void Node::update(float dt) { onUpdate(dt); } /** * @brief 渲染节点 * @param renderer 渲染后端引用 */ -void Node::render(RenderBackend& renderer) { - if (childrenOrderDirty_) { - sortChildren(); - } - onRender(renderer); +void Node::render(RenderBackend &renderer) { + if (childrenOrderDirty_) { + sortChildren(); + } + onRender(renderer); } /** * @brief 对子节点按Z序排序 */ void Node::sortChildren() { - size_t n = children_.size(); - if (n <= 1) { - childrenOrderDirty_ = false; - return; - } - - if (n < 32) { - for (size_t i = 1; i < n; ++i) { - auto key = children_[i]; - int keyZOrder = key->getZOrder(); - int j = static_cast(i) - 1; - - while (j >= 0 && children_[j]->getZOrder() > keyZOrder) { - children_[j + 1] = children_[j]; - --j; - } - children_[j + 1] = key; - } - } else { - std::sort(children_.begin(), children_.end(), - [](const Ptr& a, const Ptr& b) { - return a->getZOrder() < b->getZOrder(); - }); - } - + size_t n = children_.size(); + if (n <= 1) { childrenOrderDirty_ = false; + return; + } + + if (n < 32) { + for (size_t i = 1; i < n; ++i) { + auto key = children_[i]; + int keyZOrder = key->getZOrder(); + int j = static_cast(i) - 1; + + while (j >= 0 && children_[j]->getZOrder() > keyZOrder) { + children_[j + 1] = children_[j]; + --j; + } + children_[j + 1] = key; + } + } else { + std::sort(children_.begin(), children_.end(), + [](const Ptr &a, const Ptr &b) { + return a->getZOrder() < b->getZOrder(); + }); + } + + childrenOrderDirty_ = false; } /** @@ -691,50 +666,50 @@ void Node::sortChildren() { * @param commands 渲染命令输出向量 * @param parentZOrder 父节点的Z序 */ -void Node::collectRenderCommands(std::vector& commands, +void Node::collectRenderCommands(std::vector &commands, int parentZOrder) { - if (!visible_) - return; + if (!visible_) + return; - int accumulatedZOrder = parentZOrder + zOrder_; + int accumulatedZOrder = parentZOrder + zOrder_; - generateRenderCommand(commands, accumulatedZOrder); + generateRenderCommand(commands, accumulatedZOrder); - // 让组件也生成渲染命令 - for (auto& pair : components_) { - if (pair.second->isEnabled()) { - // 组件的渲染命令收集需要通过 RenderQueue - // 这里暂时跳过,使用 collectRenderCommandsToQueue 方法 - } + // 让组件也生成渲染命令 + for (auto &pair : components_) { + if (pair.second->isEnabled()) { + // 组件的渲染命令收集需要通过 RenderQueue + // 这里暂时跳过,使用 collectRenderCommandsToQueue 方法 } + } - for (auto& child : children_) { - child->collectRenderCommands(commands, accumulatedZOrder); - } + for (auto &child : children_) { + child->collectRenderCommands(commands, accumulatedZOrder); + } } /** * @brief 收集渲染命令到队列 * @param queue 渲染队列引用 */ -void Node::collectRenderCommandsToQueue(RenderQueue& queue) { - if (!visible_) - return; +void Node::collectRenderCommandsToQueue(RenderQueue &queue) { + if (!visible_) + return; - // 生成当前节点的渲染命令 - generateRenderCommandToQueue(queue); + // 生成当前节点的渲染命令 + generateRenderCommandToQueue(queue); - // 让组件也生成渲染命令 - for (auto& pair : components_) { - if (pair.second->isEnabled()) { - pair.second->collectRenderCommands(queue); - } + // 让组件也生成渲染命令 + for (auto &pair : components_) { + if (pair.second->isEnabled()) { + pair.second->collectRenderCommands(queue); } + } - // 递归收集子节点的渲染命令 - for (auto& child : children_) { - child->collectRenderCommandsToQueue(queue); - } + // 递归收集子节点的渲染命令 + for (auto &child : children_) { + child->collectRenderCommandsToQueue(queue); + } } } // namespace extra2d diff --git a/Extra2D/src/scene/scene.cpp b/Extra2D/src/scene/scene.cpp index ed9014a..91fd0ae 100644 --- a/Extra2D/src/scene/scene.cpp +++ b/Extra2D/src/scene/scene.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include diff --git a/Extra2D/src/scene/scene_manager.cpp b/Extra2D/src/scene/scene_manager.cpp index 497d11c..7c725d8 100644 --- a/Extra2D/src/scene/scene_manager.cpp +++ b/Extra2D/src/scene/scene_manager.cpp @@ -1,8 +1,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include diff --git a/Extra2D/src/scene/shape_node.cpp b/Extra2D/src/scene/shape_node.cpp index 676bc78..8c6da0e 100644 --- a/Extra2D/src/scene/shape_node.cpp +++ b/Extra2D/src/scene/shape_node.cpp @@ -1,7 +1,7 @@ #include #include -#include -#include +#include +#include #include #include @@ -196,24 +196,18 @@ Ptr ShapeNode::createFilledPolygon(const std::vector &points, * @brief 设置形状的所有顶点 * @param points 顶点坐标数组 */ -void ShapeNode::setPoints(const std::vector &points) { - points_ = points; -} +void ShapeNode::setPoints(const std::vector &points) { points_ = points; } /** * @brief 添加一个顶点到形状 * @param point 要添加的顶点坐标 */ -void ShapeNode::addPoint(const Vec2 &point) { - points_.push_back(point); -} +void ShapeNode::addPoint(const Vec2 &point) { points_.push_back(point); } /** * @brief 清除所有顶点 */ -void ShapeNode::clearPoints() { - points_.clear(); -} +void ShapeNode::clearPoints() { points_.clear(); } /** * @brief 获取形状的边界矩形 @@ -319,7 +313,7 @@ void ShapeNode::onDraw(RenderBackend &renderer) { case ShapeType::Triangle: if (points_.size() >= 3) { if (filled_) { - renderer.fillTriangle(points_[0], points_[1], points_[2], color_); + renderer.fillPolygon(points_.data(), 3, color_); } else { renderer.drawLine(points_[0], points_[1], color_, lineWidth_); renderer.drawLine(points_[1], points_[2], color_, lineWidth_); @@ -331,9 +325,9 @@ void ShapeNode::onDraw(RenderBackend &renderer) { case ShapeType::Polygon: if (!points_.empty()) { if (filled_) { - renderer.fillPolygon(points_, color_); + renderer.fillPolygon(points_.data(), points_.size(), color_); } else { - renderer.drawPolygon(points_, color_, lineWidth_); + renderer.drawPolygon(points_.data(), points_.size(), color_, lineWidth_); } } break; @@ -354,71 +348,44 @@ void ShapeNode::generateRenderCommand(std::vector &commands, } Vec2 offset = getPosition(); - RenderCommand cmd; - cmd.layer = zOrder; switch (shapeType_) { case ShapeType::Point: if (!points_.empty()) { - cmd.type = RenderCommandType::FilledCircle; - cmd.data = - CircleCommandData{points_[0] + offset, lineWidth_ * 0.5f, color_, 8, 0.0f, true}; + commands.push_back(RenderCommand::makeCircle( + points_[0] + offset, lineWidth_ * 0.5f, color_, 8, true, zOrder)); } break; case ShapeType::Line: if (points_.size() >= 2) { - cmd.type = RenderCommandType::Line; - cmd.data = LineCommandData{points_[0] + offset, points_[1] + offset, color_, - lineWidth_}; + commands.push_back(RenderCommand::makeLine( + points_[0] + offset, points_[1] + offset, color_, lineWidth_, zOrder)); } break; case ShapeType::Rect: if (points_.size() >= 4) { - if (filled_) { - cmd.type = RenderCommandType::FilledRect; - Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x, - points_[2].y - points_[0].y); - cmd.data = - RectCommandData{Rect(rect.origin + offset, rect.size), color_, 0.0f, true}; - } else { - cmd.type = RenderCommandType::Rect; - Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x, - points_[2].y - points_[0].y); - cmd.data = - RectCommandData{Rect(rect.origin + offset, rect.size), color_, lineWidth_, false}; - } + Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x, + points_[2].y - points_[0].y); + commands.push_back(RenderCommand::makeRect( + Rect(rect.origin + offset, rect.size), color_, lineWidth_, filled_, zOrder)); } break; case ShapeType::Circle: if (points_.size() >= 2) { float radius = points_[1].x; - if (filled_) { - cmd.type = RenderCommandType::FilledCircle; - cmd.data = - CircleCommandData{points_[0] + offset, radius, color_, segments_, 0.0f, true}; - } else { - cmd.type = RenderCommandType::Circle; - cmd.data = CircleCommandData{points_[0] + offset, radius, color_, segments_, - lineWidth_, false}; - } + commands.push_back(RenderCommand::makeCircle( + points_[0] + offset, radius, color_, segments_, filled_, zOrder)); } break; case ShapeType::Triangle: if (points_.size() >= 3) { - Vec2 p1 = points_[0] + offset; - Vec2 p2 = points_[1] + offset; - Vec2 p3 = points_[2] + offset; - if (filled_) { - cmd.type = RenderCommandType::FilledTriangle; - cmd.data = TriangleCommandData{p1, p2, p3, color_, 0.0f, true}; - } else { - cmd.type = RenderCommandType::Triangle; - cmd.data = TriangleCommandData{p1, p2, p3, color_, lineWidth_, false}; - } + commands.push_back(RenderCommand::makeTriangle( + points_[0] + offset, points_[1] + offset, points_[2] + offset, + color_, filled_, zOrder)); } break; @@ -429,19 +396,11 @@ void ShapeNode::generateRenderCommand(std::vector &commands, for (const auto &p : points_) { transformedPoints.push_back(p + offset); } - - if (filled_) { - cmd.type = RenderCommandType::FilledPolygon; - cmd.data = PolygonCommandData{transformedPoints, color_, 0.0f, true}; - } else { - cmd.type = RenderCommandType::Polygon; - cmd.data = PolygonCommandData{transformedPoints, color_, lineWidth_, false}; - } + commands.push_back(RenderCommand::makePolygon( + transformedPoints, color_, filled_, zOrder)); } break; } - - commands.push_back(std::move(cmd)); } } // namespace extra2d diff --git a/Extra2D/src/scene/sprite.cpp b/Extra2D/src/scene/sprite.cpp index eae5658..fc640c3 100644 --- a/Extra2D/src/scene/sprite.cpp +++ b/Extra2D/src/scene/sprite.cpp @@ -1,8 +1,7 @@ #include #include -#include -#include -#include +#include +#include #include namespace extra2d { @@ -168,8 +167,8 @@ void Sprite::onDraw(RenderBackend &renderer) { // 从世界变换矩阵中提取旋转角度 float worldRotation = std::atan2(worldTransform[0][1], worldTransform[0][0]); - renderer.drawSprite(*texture_, destRect, srcRect, color_, worldRotation, - anchor); + renderer.drawSprite(texture_->getRHITexture(), destRect, srcRect, color_, worldRotation, + flipX_, flipY_); } /** @@ -225,8 +224,8 @@ void Sprite::generateRenderCommand(std::vector &commands, RenderCommand cmd; cmd.type = RenderCommandType::Sprite; cmd.layer = zOrder; - cmd.data = SpriteCommandData{texture_.get(), destRect, srcRect, color_, - worldRotation, anchor, 0}; + cmd.data = SpriteRenderData{texture_->getRHITexture(), destRect, srcRect, color_, + worldRotation, anchor, rhi::BlendState::alphaBlend(), 0}; commands.push_back(std::move(cmd)); } diff --git a/Extra2D/src/scene/transition_box_scene.cpp b/Extra2D/src/scene/transition_box_scene.cpp index e4e0fe0..2922faa 100644 --- a/Extra2D/src/scene/transition_box_scene.cpp +++ b/Extra2D/src/scene/transition_box_scene.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include @@ -76,7 +76,8 @@ void TransitionBoxScene::renderContent(RenderBackend &renderer) { glm::mat4 overlayVP = glm::ortho(0.0f, windowWidth, windowHeight, 0.0f, -1.0f, 1.0f); - renderer.setViewProjection(overlayVP); + renderer.setProjectionMatrix(overlayVP); + renderer.setViewMatrix(glm::mat4(1.0f)); for (int idx = visible; idx < total; ++idx) { int x = idx % div; diff --git a/Extra2D/src/scene/transition_fade_scene.cpp b/Extra2D/src/scene/transition_fade_scene.cpp index ef26fec..3f5d702 100644 --- a/Extra2D/src/scene/transition_fade_scene.cpp +++ b/Extra2D/src/scene/transition_fade_scene.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -75,7 +75,8 @@ void TransitionFadeScene::renderContent(RenderBackend &renderer) { glm::mat4 overlayVP = glm::ortho(0.0f, windowWidth, windowHeight, 0.0f, -1.0f, 1.0f); - renderer.setViewProjection(overlayVP); + renderer.setProjectionMatrix(overlayVP); + renderer.setViewMatrix(glm::mat4(1.0f)); Color maskColor = maskColor_; maskColor.a = maskAlpha; diff --git a/Extra2D/src/scene/transition_flip_scene.cpp b/Extra2D/src/scene/transition_flip_scene.cpp index 7b0119a..3a1ab5e 100644 --- a/Extra2D/src/scene/transition_flip_scene.cpp +++ b/Extra2D/src/scene/transition_flip_scene.cpp @@ -1,7 +1,7 @@ #include #include -#include -#include +#include +#include namespace extra2d { diff --git a/Extra2D/src/scene/transition_scale_scene.cpp b/Extra2D/src/scene/transition_scale_scene.cpp index 7ca6de6..a3b4248 100644 --- a/Extra2D/src/scene/transition_scale_scene.cpp +++ b/Extra2D/src/scene/transition_scale_scene.cpp @@ -1,6 +1,6 @@ #include -#include -#include +#include +#include #include namespace extra2d { diff --git a/Extra2D/src/scene/transition_scene.cpp b/Extra2D/src/scene/transition_scene.cpp index a5b6df2..825a33e 100644 --- a/Extra2D/src/scene/transition_scene.cpp +++ b/Extra2D/src/scene/transition_scene.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include diff --git a/Extra2D/src/scene/transition_slide_scene.cpp b/Extra2D/src/scene/transition_slide_scene.cpp index 85d7f0b..fd1c14f 100644 --- a/Extra2D/src/scene/transition_slide_scene.cpp +++ b/Extra2D/src/scene/transition_slide_scene.cpp @@ -1,6 +1,6 @@ #include -#include -#include +#include +#include #include namespace extra2d { diff --git a/xmake/engine.lua b/xmake/engine.lua index 7a4cfd2..d3124ea 100644 --- a/xmake/engine.lua +++ b/xmake/engine.lua @@ -34,13 +34,13 @@ function define_extra2d_engine() -- 渲染后端源文件 local render_backend = get_render_backend() - -- 图形后端工厂(始终编译) - add_files("Extra2D/src/graphics/backends/backend_factory.cpp") + -- 渲染后端工厂 + add_files("Extra2D/src/render/core/backend_factory.cpp") if render_backend == "vulkan" then - add_files("Extra2D/src/graphics/backends/vulkan/*.cpp") + add_files("Extra2D/src/render/backends/vulkan/*.cpp") add_defines("E2D_BACKEND_VULKAN") else - add_files("Extra2D/src/graphics/backends/opengl/*.cpp") + add_files("Extra2D/src/render/backends/opengl/*.cpp") add_files("Extra2D/src/glad/glad.c") add_defines("E2D_BACKEND_OPENGL") end