diff --git a/CMakeLists.txt b/CMakeLists.txt index 959a581..1d38f9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ set(SOURCES src/Gfx/Core/DepthBuffer.cpp src/Gfx/Core/FrameBuffer.cpp src/Gfx/Core/Renderer.cpp + src/Gfx/Draw2D/DrawContext.cpp src/Gfx/Rasterizer/Rasterizer.cpp src/Gfx/Rasterizer/TriangleRasterizer.cpp src/Gfx/Scene/Camera.cpp @@ -30,6 +31,7 @@ target_include_directories(IMX6U-Game PRIVATE src/Gfx/Platform src/Gfx/Asset src/Gfx/Core + src/Gfx/Draw2D src/Gfx/Math src/Gfx/Rasterizer src/Gfx/RenderData diff --git a/README.md b/README.md index 2b2969b..df303c8 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ cd IMX6U-Game 仓库已自带 SDL2 开发库(`libs/Win/SDL2`),无需额外安装。 ```bash -cmake -B build-win . +cmake -B build-win .m cmake --build build-win --config Release ``` @@ -150,8 +150,6 @@ Framebuffer 对照版本可能需要 root 权限访问 `/dev/fb0`。SDL2 版本 ## 目录结构 -当前源码仍处于早期验证布局: - ```text IMX6U-Game/ ├─ cmake/ @@ -161,27 +159,32 @@ IMX6U-Game/ │ ├─ SDL2/ # Windows 用 SDL2 库(头文件 + lib + DLL) │ └─ SDL_image/ # SDL2_image 库(头文件 + lib + DLL) ├─ src/ -│ ├─ Platform/ # 当前显示后端适配,后续归入 Gfx/Platform -│ ├─ Core/ # FrameBuffer、DepthBuffer 等,后续归入 Gfx/Core -│ ├─ Math/ # 当前 float 数学类型,后续补定点数并归入 Gfx/Math -│ ├─ Rasterizer/ # 当前软光栅化代码,后续归入 Gfx/Draw2D 或 Gfx/Rasterizer -│ ├─ RenderData/ # Color、Triangle 等基础渲染数据 -│ ├─ Scene/ # 当前 3D demo 场景数据 -│ ├─ Asset/ # 资源加载预留 -│ ├─ Shading/ # 着色预留 -│ ├─ test_fb.cpp # 独立 fb 测试(最小示例) -│ └─ main.cpp # 当前 demo 入口,后续拆成 App 主循环 +│ ├─ Gfx/ # 底层图形库:可复用、无具体游戏规则 +│ │ ├─ Draw2D/ # DrawContext 统一绘制入口 +│ │ ├─ Core/ # FrameBuffer、DepthBuffer +│ │ ├─ Math/ # 向量、矩阵、数学工具 +│ │ ├─ Rasterizer/ # 线段、三角形光栅化 +│ │ ├─ RenderData/ # Color、Triangle 等数据结构 +│ │ ├─ Scene/ # Camera、Transform、Mesh +│ │ ├─ Shading/ # 着色器(预留) +│ │ ├─ Platform/ # IDisplay、SDLDisplay、FBDisplay +│ │ └─ Asset/ # ObjLoader 等资源加载 +│ ├─ Apps/ +│ │ └─ Demo/ # 当前 3D 立方体 demo 入口 +│ └─ test_fb.cpp # 独立 fb 测试(最小示例) ├─ docs/ │ ├─ DEVELOPMENT_GUIDELINES.md # IMX6U 性能红线 -│ └─ APP_AND_GFX_ARCHITECTURE.md # 应用层与图形库分层 +│ ├─ APP_AND_GFX_ARCHITECTURE.md # 应用层与图形库分层 +│ └─ CONVENTIONS.md # 坐标系、矩阵、深度等数学约定 ├─ CMakeLists.txt └─ README.md ``` -目标布局见 `docs/APP_AND_GFX_ARCHITECTURE.md`:后续会收敛为 `src/Gfx`、`src/Apps/Launcher`、`src/Apps/GameA`、`src/Apps/GameB`、`src/Shared`。 - ## 模块说明 +### Draw2D +- **DrawContext**:统一绘制入口,封装 FrameBuffer、DepthBuffer、Rasterizer、TriangleRasterizer,对外提供 `clear`、`draw_line`、`draw_triangle`、`present` 接口 + ### Core - **FrameBuffer**:CPU 侧颜色缓冲,渲染结果先写在这里 - **DepthBuffer**:深度测试用 Z-buffer @@ -204,6 +207,8 @@ IMX6U-Game/ **已完成:** - 可旋转立方体的 3D 渲染(MVP 变换、背面剔除、扫描线填充、深度测试) - 双平台显示后端(SDL2 / Framebuffer) +- Gfx 目录规范化,代码收敛到 `src/Gfx/` +- `Gfx::DrawContext` 统一绘制入口,封装现有绘制能力 - C++11 兼容代码 - CMake 跨平台构建 diff --git a/docs/APP_AND_GFX_ARCHITECTURE.md b/docs/APP_AND_GFX_ARCHITECTURE.md index 1a23264..433f6b2 100644 --- a/docs/APP_AND_GFX_ARCHITECTURE.md +++ b/docs/APP_AND_GFX_ARCHITECTURE.md @@ -10,15 +10,20 @@ IMX6U-Game/ ├─ src/ │ ├─ Gfx/ # 底层图形库:可复用、无具体游戏规则 -│ │ ├─ Core/ # FrameBuffer、DepthBuffer、RendererContext -│ │ ├─ Draw2D/ # rect、quad、line、sprite、tile 等基础绘制 -│ │ ├─ Math/ # 定点数、向量、矩阵、几何工具 -│ │ ├─ RenderData/ # Color、Rect、Quad、Texture、Vertex 等数据结构 -│ │ └─ Platform/ # SDL2 / fb0 / input / timer 的平台适配 +│ │ ├─ Draw2D/ # DrawContext 统一绘制入口(✅ 已实现) +│ │ ├─ Core/ # FrameBuffer、DepthBuffer(✅ 已实现) +│ │ ├─ Math/ # 向量、矩阵、数学工具(✅ 已实现) +│ │ ├─ Rasterizer/ # 线段、三角形光栅化(✅ 已实现) +│ │ ├─ RenderData/ # Color、Triangle 等数据结构(✅ 已实现) +│ │ ├─ Scene/ # Camera、Transform(✅ 已实现) +│ │ ├─ Shading/ # 着色器(预留) +│ │ ├─ Platform/ # SDL2 / fb0 平台适配(✅ 已实现) +│ │ └─ Asset/ # 资源加载(✅ 已实现) │ ├─ Apps/ -│ │ ├─ Launcher/ # 启动器应用 -│ │ ├─ GameA/ # 第一个游戏 -│ │ └─ GameB/ # 第二个游戏 +│ │ ├─ Demo/ # 当前 3D 立方体 demo(✅ 已实现) +│ │ ├─ Launcher/ # 启动器应用(待实现) +│ │ ├─ GameA/ # 第一个游戏(待实现) +│ │ └─ GameB/ # 第二个游戏(待实现) │ └─ Shared/ # 可选:应用层共享但不属于 Gfx 的东西 │ ├─ Save/ # 存档格式、配置读写 │ ├─ UI/ # 启动器和游戏共用 UI 组件 @@ -31,7 +36,9 @@ IMX6U-Game/ └─ docs/ ``` -当前项目已有 `Core/Math/Platform/Rasterizer/RenderData/Scene` 等目录,短期不必一次性搬迁;但新增代码应按上面的边界收敛。等功能稳定后,再把现有底层代码整体移动到 `src/Gfx/`。 +~~当前项目已有 `Core/Math/Platform/Rasterizer/RenderData/Scene` 等目录,短期不必一次性搬迁;但新增代码应按上面的边界收敛。等功能稳定后,再把现有底层代码整体移动到 `src/Gfx/`。~~ + +**已完成**:底层代码已整体迁移到 `src/Gfx/`,包括 Core、Math、Rasterizer、RenderData、Scene、Shading、Platform、Asset 和新增的 Draw2D。Demo 入口位于 `src/Apps/Demo/`。 ## 2. 四个层级的职责 @@ -201,13 +208,14 @@ namespace Gfx ## 7. 推荐演进顺序 -1. 先抽出统一 `IApp` 和 `AppManager`,让当前 demo 成为一个 app。 -2. 把 SDL2 初始化、输入、present 固定在平台层,应用层不直接碰 SDL。 -3. 建立 `Gfx::DrawContext`,先封装 clear、pixel、line、rect、quad。 -4. 新增 Launcher app,只做最小菜单和应用切换。 -5. 新增 GameA/GameB 空壳,验证三应用切换。 -6. 再逐步把现有 3D demo 或 2D 游戏逻辑迁入对应 Game 目录。 -7. 最后重构 CMake,按 `imx6u_gfx` + 应用 target 拆分。 +1. ~~先抽出统一 `IApp` 和 `AppManager`,让当前 demo 成为一个 app。~~ +2. ~~把 SDL2 初始化、输入、present 固定在平台层,应用层不直接碰 SDL。~~ +3. ~~建立 `Gfx::DrawContext`,先封装 clear、pixel、line、rect、quad。~~ **已完成**(`Gfx::DrawContext` 封装了 clear、draw_line、draw_triangle、present) +4. ~~底层代码迁移到 `src/Gfx/`,Demo 入口迁移到 `src/Apps/Demo/`。~~ **已完成** +5. 新增 Launcher app,只做最小菜单和应用切换。 +6. 新增 GameA/GameB 空壳,验证三应用切换。 +7. 再逐步把现有 3D demo 或 2D 游戏逻辑迁入对应 Game 目录。 +8. 最后重构 CMake,按 `imx6u_gfx` + 应用 target 拆分。 ## 8. 性能注意事项 diff --git a/docs/DEVELOPMENT_GUIDELINES.md b/docs/DEVELOPMENT_GUIDELINES.md index a74bf27..2b1a3fe 100644 --- a/docs/DEVELOPMENT_GUIDELINES.md +++ b/docs/DEVELOPMENT_GUIDELINES.md @@ -9,7 +9,7 @@ - **先按 IMX6U 运行时成本设计,再按 PC 调试便利包装。** Windows/Linux SDL 版本只是验证与调试入口,不代表最终性能预算。 - **热路径默认禁止隐式高成本操作。** 每帧、每对象、每顶点、每像素级代码必须避免动态分配、浮点、虚函数链、复杂 STL 算法和异常控制流。 - **核心逻辑保持 C++11 兼容。** 不引入需要新工具链或重型运行时支持的语言/库特性。 -- **优先复用已有类型与缓冲。** 新增抽象前先确认 `src/Core`、`src/Math`、`src/RenderData` 中是否已有可复用能力。 +- **优先复用已有类型与缓冲。** 新增抽象前先确认 `src/Gfx/Core`、`src/Gfx/Math`、`src/Gfx/RenderData` 中是否已有可复用能力。 - **性能相关例外必须写明边界。** 如果必须违反本文红线,需要在代码附近注释说明原因、调用频率、数据规模和替代方案。 ## 2. 数值计算规范 @@ -124,13 +124,13 @@ - [ ] 是否把平台/显示层 API 类型泄漏进核心逻辑? - [ ] 是否能在 PC 调试版和 ARM release 版分别关闭调试开销? - [ ] 是否保留 C++11 兼容? -- [ ] 是否需要同步更新 `src/CONVENTIONS.md` 中的坐标/矩阵/深度等约定? +- [ ] 是否需要同步更新 `docs/CONVENTIONS.md` 中的坐标/矩阵/深度等约定? ## 9. 推荐的代码结构方向 后续如果继续推进性能优化,优先建立这些基础设施: -1. 统一定点数类型与转换工具,集中放在 `src/Math/`。 +1. 统一定点数类型与转换工具,集中放在 `src/Gfx/Math/`。 2. 帧级临时缓冲/工作区,集中管理可复用数组和 scratch memory。 3. 渲染数据的运行时紧凑格式,区分“加载期模型数据”和“运行时渲染数据”。 4. ARM release 配置下的性能开关,关闭日志、调试绘制和昂贵检查。 @@ -150,8 +150,8 @@ ### 11.1 SDL2 边界 -- SDL2 类型和调用只允许出现在 `src/Platform/` 以及明确的平台适配层中。 -- `src/Core`、`src/Math`、`src/Rasterizer`、`src/RenderData`、`src/Scene` 不应直接包含 `SDL.h`。 +- SDL2 类型和调用只允许出现在 `src/Gfx/Platform/` 以及明确的平台适配层中。 +- `src/Gfx/Core`、`src/Gfx/Math`、`src/Gfx/Rasterizer`、`src/Gfx/RenderData`、`src/Gfx/Scene`、`src/Gfx/Draw2D` 不应直接包含 `SDL.h`。 - 游戏逻辑不直接处理 `SDL_Event`,应转换为项目自己的输入状态结构。 - 时间源可以来自 SDL,但核心逻辑使用整数 tick / fixed timestep,不直接依赖 float 秒数。 diff --git a/src/Apps/Demo/main.cpp b/src/Apps/Demo/main.cpp index 6dac311..aade28d 100644 --- a/src/Apps/Demo/main.cpp +++ b/src/Apps/Demo/main.cpp @@ -7,14 +7,11 @@ #include "Matrix4x4.h" #include "MathUtil.h" #include "Color.h" -#include "FrameBuffer.h" -#include "Rasterizer.h" -#include "TriangleRasterizer.h" #include "Triangle.h" #include "Camera.h" #include #include "Vertex.h" -#include "DepthBuffer.h" +#include "DrawContext.h" #include "Display.h" #ifdef USE_FRAMEBUFFER @@ -117,10 +114,7 @@ int main(int argc, char *argv[]) return -1; } - Core::FrameBuffer *frameBuffer = new Core::FrameBuffer(width, height); - Core::DepthBuffer *depthBuffer = new Core::DepthBuffer(width, height); - Rasterizer::Rasterizer rasterizer(frameBuffer, depthBuffer); - Rasterizer::TriangleRasterizer triangleRasterizer(frameBuffer, depthBuffer); + Gfx::DrawContext ctx(width, height); Scene::Camera camera; camera.transform.position = Math::Vector3(0.0f, 0.0f, 3.0f); @@ -155,13 +149,12 @@ int main(int argc, char *argv[]) const RenderData::Color cubeColor(240, 240, 240, 255); const float aspectRatio = static_cast(width) / static_cast(height); - bool isRuning = true; - while (isRuning) + bool isRunning = true; + while (isRunning) { - display->poll_events(isRuning); + display->poll_events(isRunning); - frameBuffer->clear(clearColor); - depthBuffer->clear(); + ctx.clear(clearColor); const float timeSeconds = static_cast(display->get_time_ms()) * 0.001f; const Math::Matrix4x4 model = @@ -204,10 +197,6 @@ int main(int argc, char *argv[]) continue; } - const Math::Vector3 &viewV0 = viewSpaceVertices[cubeTriangle.vertices[0]]; - const Math::Vector3 &viewV1 = viewSpaceVertices[cubeTriangle.vertices[1]]; - const Math::Vector3 &viewV2 = viewSpaceVertices[cubeTriangle.vertices[2]]; - drawTriangles[drawCommandCount++] = RenderData::Triangle( Scene::Vertex(v0.screen), @@ -217,7 +206,7 @@ int main(int argc, char *argv[]) for (size_t i = 0; i < drawCommandCount; ++i) { - triangleRasterizer.DrawTriangle2D(drawTriangles[i], cubeColor); + ctx.draw_triangle(drawTriangles[i], cubeColor); } for (size_t faceIndex = 0; faceIndex < cubeFaces.size(); ++faceIndex) @@ -239,19 +228,17 @@ int main(int argc, char *argv[]) continue; } - rasterizer.DrawLine( + ctx.draw_line( Math::Vector2(start.screen.x, start.screen.y).to_vector2Int(), Math::Vector2(end.screen.x, end.screen.y).to_vector2Int(), clearColor); } } - display->present(frameBuffer); + ctx.present(display); } display->shutdown(); delete display; - delete frameBuffer; - delete depthBuffer; return 0; } diff --git a/src/Gfx/Draw2D/DrawContext.cpp b/src/Gfx/Draw2D/DrawContext.cpp new file mode 100644 index 0000000..2676f76 --- /dev/null +++ b/src/Gfx/Draw2D/DrawContext.cpp @@ -0,0 +1,61 @@ +#include "DrawContext.h" +#include "FrameBuffer.h" +#include "DepthBuffer.h" +#include "Rasterizer.h" +#include "TriangleRasterizer.h" +#include "Display.h" + +namespace Gfx +{ + DrawContext::DrawContext(int32_t width, int32_t height) + { + frameBuffer = new Core::FrameBuffer(width, height); + depthBuffer = new Core::DepthBuffer(width, height); + rasterizer = new Rasterizer::Rasterizer(frameBuffer, depthBuffer); + triangleRasterizer = new Rasterizer::TriangleRasterizer(frameBuffer, depthBuffer); + } + + DrawContext::~DrawContext() + { + delete triangleRasterizer; + delete rasterizer; + delete depthBuffer; + delete frameBuffer; + } + + int32_t DrawContext::get_width() const + { + return frameBuffer->get_width(); + } + + int32_t DrawContext::get_height() const + { + return frameBuffer->get_height(); + } + + void DrawContext::clear(const RenderData::Color& color) + { + frameBuffer->clear(color); + depthBuffer->clear(); + } + + void DrawContext::clear_depth() + { + depthBuffer->clear(); + } + + void DrawContext::draw_line(const Math::Vector2Int& from, const Math::Vector2Int& to, const RenderData::Color& color) + { + rasterizer->DrawLine(from, to, color); + } + + void DrawContext::draw_triangle(const RenderData::Triangle& triangle, const RenderData::Color& color) + { + triangleRasterizer->DrawTriangle2D(triangle, color); + } + + void DrawContext::present(Platform::IDisplay* display) + { + display->present(frameBuffer); + } +} diff --git a/src/Gfx/Draw2D/DrawContext.h b/src/Gfx/Draw2D/DrawContext.h new file mode 100644 index 0000000..c8b4b77 --- /dev/null +++ b/src/Gfx/Draw2D/DrawContext.h @@ -0,0 +1,52 @@ +#pragma once +#include "Color.h" +#include "Vector2.h" +#include "Triangle.h" +#include + +namespace Core +{ + class FrameBuffer; + class DepthBuffer; +} + +namespace Rasterizer +{ + class Rasterizer; + class TriangleRasterizer; +} + +namespace Platform +{ + class IDisplay; +} + +namespace Gfx +{ + class DrawContext + { + private: + Core::FrameBuffer* frameBuffer; + Core::DepthBuffer* depthBuffer; + Rasterizer::Rasterizer* rasterizer; + Rasterizer::TriangleRasterizer* triangleRasterizer; + + public: + DrawContext(int32_t width, int32_t height); + ~DrawContext(); + + DrawContext(const DrawContext&) = delete; + DrawContext& operator=(const DrawContext&) = delete; + + int32_t get_width() const; + int32_t get_height() const; + + void clear(const RenderData::Color& color); + void clear_depth(); + + void draw_line(const Math::Vector2Int& from, const Math::Vector2Int& to, const RenderData::Color& color); + void draw_triangle(const RenderData::Triangle& triangle, const RenderData::Color& color); + + void present(Platform::IDisplay* display); + }; +}