# GroveEngine - RenderModule (Framework Module) **Type** : Framework Module (base avancée) **Stack** : bgfx + SDL2 **Objectif** : Window + UI 2D element rendering **Dernière mise à jour** : 26 novembre 2025 --- ## Vue d'ensemble Module framework pour GroveEngine fournissant les capacités de rendu 2D de base : - **Window management** via SDL2 - **Rendering 2D** via bgfx (multi-backend : Vulkan/DX12/Metal/OpenGL) - **UI element rendering** (primitives 2D) **Base avancée** = Intégration complète dans le GroveEngine + capacités rendering fonctionnelles --- ## Architecture Module ### Integration GroveEngine ```cpp class RenderModule : public grove::IModule { public: // IModule interface void initialize(const IDataNode& config, IIO* io) override; void update(float dt) override; void shutdown() override; // Rendering API void BeginFrame(); void EndFrame(); // 2D Primitives void DrawRect(const Rect& rect, Color color); void DrawSprite(const Sprite& sprite); void DrawText(const std::string& text, Font font, Vec2 pos); private: SDL_Window* window; bgfx::ViewId main_view; Renderer2D renderer; }; ``` ### Composants Clés **1. Window Management (SDL2)** ```cpp struct WindowConfig { int width; int height; std::string title; bool fullscreen; bool vsync; }; // Création window SDL2 window = SDL_CreateWindow( config.title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, config.width, config.height, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE ); ``` **2. bgfx Initialization** ```cpp bgfx::Init init; init.type = bgfx::RendererType::Count; // Auto-detect init.resolution.width = width; init.resolution.height = height; init.resolution.reset = BGFX_RESET_VSYNC; // Utilise window SDL2 SDL_SysWMinfo wmi; SDL_VERSION(&wmi.version); SDL_GetWindowWMInfo(window, &wmi); #if BX_PLATFORM_WINDOWS init.platformData.nwh = wmi.info.win.window; #elif BX_PLATFORM_LINUX init.platformData.ndt = wmi.info.x11.display; init.platformData.nwh = (void*)(uintptr_t)wmi.info.x11.window; #endif bgfx::init(init); ``` **3. Renderer2D (Primitives)** ```cpp class Renderer2D { public: void DrawRect(const Rect& rect, Color color); void DrawSprite(const Sprite& sprite); void Flush(); // Submit batched draws private: bgfx::ProgramHandle sprite_shader; bgfx::ProgramHandle color_shader; struct SpriteBatch { std::vector vertices; bgfx::TextureHandle texture; }; std::vector batches; }; ``` --- ## Rendering Pipeline ### Frame Structure ```cpp void RenderModule::update(float dt) { BeginFrame(); // Rendering commands DrawRect({100, 100, 200, 150}, Color::Red()); DrawSprite(test_sprite); DrawText("Hello GroveEngine", main_font, {50, 50}); EndFrame(); } void RenderModule::BeginFrame() { bgfx::touch(main_view); bgfx::setViewClear(main_view, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x303030ff, 1.0f, 0); } void RenderModule::EndFrame() { renderer.Flush(); // Submit batched draws bgfx::frame(); // Advance frame } ``` ### Vertex Formats **Sprite Vertex (textured quad)** ```cpp struct SpriteVertex { float x, y, z; // Position float u, v; // UV coords uint32_t color; // Tint (ABGR) }; bgfx::VertexLayout sprite_layout; sprite_layout.begin() .add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float) .add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float) .add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true) .end(); ``` **Color Vertex (solid color quad)** ```cpp struct ColorVertex { float x, y, z; uint32_t color; }; bgfx::VertexLayout color_layout; color_layout.begin() .add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float) .add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true) .end(); ``` --- ## Shaders ### Sprite Shader (Vertex) ```glsl // vs_sprite.sc $input a_position, a_texcoord0, a_color0 $output v_texcoord0, v_color0 #include void main() { gl_Position = mul(u_modelViewProj, vec4(a_position, 1.0)); v_texcoord0 = a_texcoord0; v_color0 = a_color0; } ``` ### Sprite Shader (Fragment) ```glsl // fs_sprite.sc $input v_texcoord0, v_color0 #include SAMPLER2D(s_texture, 0); void main() { vec4 texColor = texture2D(s_texture, v_texcoord0); gl_FragColor = texColor * v_color0; } ``` **Compilation** : ```bash shaderc -f vs_sprite.sc -o vs_sprite.bin --type vertex --platform linux -p 120 shaderc -f fs_sprite.sc -o fs_sprite.bin --type fragment --platform linux -p 120 ``` --- ## Camera 2D ### Orthographic Projection ```cpp class Camera2D { public: Vec2 position; float zoom = 1.0f; Mat4 GetViewMatrix() const { return Mat4::Translation(-position.x, -position.y, 0); } Mat4 GetProjectionMatrix(int width, int height) const { float halfW = (width / zoom) * 0.5f; float halfH = (height / zoom) * 0.5f; return Mat4::Ortho(-halfW, halfW, -halfH, halfH, -1, 1); } }; // Setup view transform Mat4 view = camera.GetViewMatrix(); Mat4 proj = camera.GetProjectionMatrix(screenW, screenH); bgfx::setViewTransform(main_view, view.data, proj.data); ``` --- ## UI Element Rendering ### DrawRect Implementation ```cpp void Renderer2D::DrawRect(const Rect& rect, Color color) { ColorVertex vertices[4] = { {rect.x, rect.y, 0, color.abgr()}, {rect.x + rect.w, rect.y, 0, color.abgr()}, {rect.x + rect.w, rect.y + rect.h, 0, color.abgr()}, {rect.x, rect.y + rect.h, 0, color.abgr()} }; uint16_t indices[6] = {0, 1, 2, 0, 2, 3}; bgfx::TransientVertexBuffer tvb; bgfx::TransientIndexBuffer tib; bgfx::allocTransientVertexBuffer(&tvb, 4, color_layout); bgfx::allocTransientIndexBuffer(&tib, 6); memcpy(tvb.data, vertices, sizeof(vertices)); memcpy(tib.data, indices, sizeof(indices)); bgfx::setVertexBuffer(0, &tvb); bgfx::setIndexBuffer(&tib); bgfx::setState(BGFX_STATE_DEFAULT); bgfx::submit(main_view, color_shader); } ``` ### DrawSprite Implementation (Batched) ```cpp void Renderer2D::DrawSprite(const Sprite& sprite) { // Find or create batch for this texture SpriteBatch* batch = GetBatchForTexture(sprite.texture); // Add quad to batch float x0 = sprite.pos.x; float y0 = sprite.pos.y; float x1 = x0 + sprite.size.x; float y1 = y0 + sprite.size.y; uint32_t color = sprite.tint.abgr(); batch->vertices.push_back({x0, y0, 0, 0, 0, color}); batch->vertices.push_back({x1, y0, 0, 1, 0, color}); batch->vertices.push_back({x1, y1, 0, 1, 1, color}); batch->vertices.push_back({x0, y1, 0, 0, 1, color}); } void Renderer2D::Flush() { for (auto& batch : batches) { size_t quad_count = batch.vertices.size() / 4; // Generate indices (0,1,2, 0,2,3 per quad) std::vector indices; for (size_t i = 0; i < quad_count; ++i) { uint16_t base = i * 4; indices.push_back(base + 0); indices.push_back(base + 1); indices.push_back(base + 2); indices.push_back(base + 0); indices.push_back(base + 2); indices.push_back(base + 3); } // Submit draw call bgfx::TransientVertexBuffer tvb; bgfx::TransientIndexBuffer tib; bgfx::allocTransientVertexBuffer(&tvb, batch.vertices.size(), sprite_layout); bgfx::allocTransientIndexBuffer(&tib, indices.size()); memcpy(tvb.data, batch.vertices.data(), batch.vertices.size() * sizeof(SpriteVertex)); memcpy(tib.data, indices.data(), indices.size() * sizeof(uint16_t)); bgfx::setTexture(0, sampler, batch.texture); bgfx::setVertexBuffer(0, &tvb); bgfx::setIndexBuffer(&tib); bgfx::setState(BGFX_STATE_DEFAULT); bgfx::submit(main_view, sprite_shader); } batches.clear(); } ``` --- ## Configuration Module ### Config File (JSON) ```json { "module": "RenderModule", "window": { "width": 1280, "height": 720, "title": "GroveEngine - RenderModule Test", "fullscreen": false, "vsync": true }, "rendering": { "clear_color": "#303030", "main_view_id": 0 } } ``` ### Loading Config ```cpp void RenderModule::initialize(const IDataNode& config, IIO* io) { WindowConfig window_config; window_config.width = config.get("window.width", 1280); window_config.height = config.get("window.height", 720); window_config.title = config.get("window.title", "GroveEngine"); window_config.vsync = config.get("window.vsync", true); InitializeWindow(window_config); InitializeBgfx(window_config); InitializeRenderer(); } ``` --- ## Roadmap Développement ### Phase 1 - Window + Init (Semaine 1) **Objectif** : Window SDL2 + bgfx initialized, clear screen - [ ] Setup SDL2 window - [ ] Initialize bgfx avec window handle - [ ] Clear screen avec couleur - [ ] Main loop fonctionnel - [ ] Event handling basique (close window) **Livrable** : Window qui affiche fond gris, ferme proprement --- ### Phase 2 - Primitives 2D (Semaine 2) **Objectif** : DrawRect fonctionnel - [ ] Vertex layouts (ColorVertex, SpriteVertex) - [ ] Shader compilation (color shader) - [ ] DrawRect implementation - [ ] Camera 2D orthographic - [ ] Afficher 1 rectangle coloré **Livrable** : Fenêtre avec rectangle rouge affiché --- ### Phase 3 - Sprite Rendering (Semaine 3) **Objectif** : DrawSprite fonctionnel - [ ] Texture loading (stb_image) - [ ] Sprite shader (vertex + fragment) - [ ] DrawSprite implementation (batched) - [ ] Afficher 1 sprite texturé **Livrable** : Fenêtre avec sprite test affiché --- ### Phase 4 - UI Elements (Semaine 4) **Objectif** : Text rendering + UI primitives - [ ] Font loading (stb_truetype ou FreeType) - [ ] DrawText implementation - [ ] UI element base class - [ ] UIPanel, UIText primitives **Livrable** : Fenêtre avec panneau UI + texte --- ## Dépendances ### Externes (Libraries) - **SDL2** : Window + input - **bgfx** : Rendering abstraction - **bx** : Base library (bgfx dependency) - **bimg** : Image loading (bgfx dependency) - **stb_image** : Texture loading - **stb_truetype** ou **FreeType** : Font rendering ### GroveEngine (Internes) - `IModule` interface - `IDataNode` (config system) - `IIO` (inter-module communication) --- ## Test Case - Base Avancée ### Validation Criteria **Window + UI 2D element = base avancée validée** si : 1. ✅ Window SDL2 créée et stable 2. ✅ bgfx initialisé et rendering fonctionnel 3. ✅ DrawRect affiche rectangle coloré 4. ✅ DrawSprite affiche sprite texturé 5. ✅ DrawText affiche texte (police) 6. ✅ UI element (panel) rendu correctement 7. ✅ Module intégré dans GroveEngine (IModule interface) ### Example Test Code ```cpp // main.cpp int main() { auto engine = grove::EngineFactory::CreateDebugEngine(); auto config = LoadConfig("render_module_config.json"); engine->LoadModule("RenderModule", config); // Game loop while (!should_quit) { engine->Update(dt); } return 0; } // Dans RenderModule::update() void RenderModule::update(float dt) { BeginFrame(); // Test: Rectangle DrawRect({100, 100, 200, 100}, Color{255, 0, 0, 255}); // Test: Sprite DrawSprite(test_sprite); // Test: UI Panel DrawRect({50, 50, 300, 200}, Color{64, 64, 64, 200}); DrawText("Base Avancée OK!", main_font, {60, 60}); EndFrame(); } ``` --- ## Questions Ouvertes ### Architecture 1. **Text rendering** : stb_truetype (simple) ou FreeType (complet) ? 2. **Asset management** : AssetManager séparé ou intégré dans RenderModule ? 3. **Multi-window** : Support futur ou single window only ? ### Performance 4. **Sprite batching** : Sort by texture ou sort by Z-order ? 5. **Transient buffers** : OK pour MVP ou besoin persistent buffers ? ### Integration 6. **ImGui** : Garder ImGuiUI.h existant ou remplacer par custom UI ? 7. **Hot-reload** : Shader hot-reload nécessaire ou compilation statique OK ? --- ## Ressources ### Documentation bgfx - Repo : https://github.com/bkaradzic/bgfx - Examples : https://github.com/bkaradzic/bgfx/tree/master/examples - API docs : https://bkaradzic.github.io/bgfx/ ### Tutorials 2D avec bgfx - bgfx example-26 (vectordisplay) - NanoVG-bgfx : https://github.com/memononen/nanovg ### SDL2 - Wiki : https://wiki.libsdl.org/ - Lazy Foo tutorials : https://lazyfoo.net/tutorials/SDL/ --- ## Liens Projets - **GroveEngine core** : `Projects/WIP/GroveEngine.md` - **GroveEngine architecture** : `Projects/WIP/grove-sup.md` - **Aissia** (utilisateur) : `Projects/WIP/AISSIA.md` - **Pokrovsk** (utilisateur futur) : `Projects/CONCEPT/pokrovsk_last_day.md` --- **Créé** : 26 novembre 2025 **Status** : Architecture planning **Type** : Framework module (base avancée) **Objectif** : Window + UI 2D rendering pour GroveEngine