Fixed two critical bugs preventing multiple textured sprites from rendering correctly: 1. **setState consumed by submit**: Render state was set once at the beginning, but bgfx consumes state at each submit(). Batches 2+ had no state → invisible. FIX: Call setState() before EACH batch, not once globally. 2. **Buffer overwrite race condition**: updateBuffer() is immediate but submit() is deferred. When batch 2 called updateBuffer(), it overwrote batch 1's data BEFORE bgfx executed the draw calls. All batches used the last batch's data → all sprites rendered at the same position (superimposed). FIX: Use transient buffers (one per batch, frame-local) instead of reusing the same dynamic buffer. Each batch gets its own isolated memory. Changes: - SpritePass: setState before each batch + transient buffer allocation per batch - UIRenderer: Retained mode rendering (render:sprite:add/update/remove) - test_ui_showcase: Added 3 textured buttons demo section - test_3buttons_minimal: Minimal test case for multi-texture debugging Tested: 3 textured buttons now render at correct positions with correct textures. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
212 lines
8.0 KiB
C++
212 lines
8.0 KiB
C++
/**
|
|
* Test: UIButton avec texture PNG chargée depuis un fichier
|
|
* Ce test montre qu'on peut mettre une VRAIE image sur un bouton
|
|
*/
|
|
|
|
#include <SDL.h>
|
|
#include <SDL_syswm.h>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <fstream>
|
|
|
|
#include "BgfxRendererModule.h"
|
|
#include "UIModule/UIModule.h"
|
|
#include "../modules/BgfxRenderer/Resources/ResourceCache.h"
|
|
#include "../modules/BgfxRenderer/RHI/RHITypes.h"
|
|
#include "../modules/BgfxRenderer/RHI/RHIDevice.h"
|
|
#include <grove/JsonDataNode.h>
|
|
#include <grove/IntraIOManager.h>
|
|
#include <grove/IntraIO.h>
|
|
#include <spdlog/spdlog.h>
|
|
#include <spdlog/sinks/stdout_color_sinks.h>
|
|
#include <bgfx/bgfx.h>
|
|
#include <vector>
|
|
|
|
using namespace grove;
|
|
|
|
int main(int argc, char* argv[]) {
|
|
spdlog::set_level(spdlog::level::info);
|
|
auto logger = spdlog::stdout_color_mt("TexturedButtonTest");
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
|
std::cerr << "SDL_Init failed" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
SDL_Window* window = SDL_CreateWindow(
|
|
"Textured Button Test - Gradient",
|
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
|
800, 600, SDL_WINDOW_SHOWN
|
|
);
|
|
|
|
SDL_SysWMinfo wmi;
|
|
SDL_VERSION(&wmi.version);
|
|
SDL_GetWindowWMInfo(window, &wmi);
|
|
|
|
// Create IIO instances - IMPORTANT: game publishes input, ui subscribes and publishes render commands
|
|
auto gameIO = IntraIOManager::getInstance().createInstance("game");
|
|
auto uiIO = IntraIOManager::getInstance().createInstance("ui");
|
|
auto rendererIO = IntraIOManager::getInstance().createInstance("renderer");
|
|
|
|
gameIO->subscribe("ui:hover");
|
|
gameIO->subscribe("ui:click");
|
|
gameIO->subscribe("ui:action");
|
|
|
|
// Initialize BgfxRenderer WITH 3 TEXTURES loaded via config
|
|
auto renderer = std::make_unique<BgfxRendererModule>();
|
|
{
|
|
JsonDataNode config("config");
|
|
config.setDouble("nativeWindowHandle",
|
|
static_cast<double>(reinterpret_cast<uintptr_t>(wmi.info.win.window)));
|
|
config.setInt("windowWidth", 800);
|
|
config.setInt("windowHeight", 600);
|
|
// Load 3 textures
|
|
config.setString("texture1", "../../assets/textures/5oxaxt1vo2f91.jpg"); // Car
|
|
config.setString("texture2", "../../assets/textures/1f440.png"); // Eyes
|
|
config.setString("texture3", "../../assets/textures/IconDesigner.png"); // Icon
|
|
|
|
renderer->setConfiguration(config, rendererIO.get(), nullptr);
|
|
}
|
|
|
|
logger->info("✓ Loaded 3 textures (IDs: 1, 2, 3)");
|
|
|
|
// Initialize UIModule with 3 TEXTURED BUTTONS
|
|
auto ui = std::make_unique<UIModule>();
|
|
{
|
|
JsonDataNode config("config");
|
|
config.setInt("windowWidth", 800);
|
|
config.setInt("windowHeight", 600);
|
|
|
|
nlohmann::json layoutJson = {
|
|
{"id", "root"},
|
|
{"type", "panel"},
|
|
{"x", 0}, {"y", 0},
|
|
{"width", 800}, {"height", 600}, // Full screen invisible panel (just container)
|
|
{"style", {
|
|
{"bgColor", "0x00000000"} // Fully transparent - just a container
|
|
}},
|
|
{"children", {
|
|
{
|
|
{"id", "btn_car"},
|
|
{"type", "button"},
|
|
{"x", 50},
|
|
{"y", 50},
|
|
{"width", 400},
|
|
{"height", 200},
|
|
{"text", ""},
|
|
{"onClick", "car_action"},
|
|
{"style", {
|
|
{"normal", {{"textureId", 1}, {"bgColor", "0xFFFFFFFF"}}},
|
|
{"hover", {{"textureId", 1}, {"bgColor", "0xFFFF00FF"}}},
|
|
{"pressed", {{"textureId", 1}, {"bgColor", "0x888888FF"}}}
|
|
}}
|
|
},
|
|
{
|
|
{"id", "btn_eyes"},
|
|
{"type", "button"},
|
|
{"x", 50},
|
|
{"y", 270},
|
|
{"width", 250},
|
|
{"height", 200},
|
|
{"text", ""},
|
|
{"onClick", "eyes_action"},
|
|
{"style", {
|
|
{"normal", {{"textureId", 2}, {"bgColor", "0xFFFFFFFF"}}},
|
|
{"hover", {{"textureId", 2}, {"bgColor", "0x00FFFFFF"}}},
|
|
{"pressed", {{"textureId", 2}, {"bgColor", "0x888888FF"}}}
|
|
}}
|
|
},
|
|
{
|
|
{"id", "btn_icon"},
|
|
{"type", "button"},
|
|
{"x", 320},
|
|
{"y", 270},
|
|
{"width", 250},
|
|
{"height", 200},
|
|
{"text", ""},
|
|
{"onClick", "icon_action"},
|
|
{"style", {
|
|
{"normal", {{"textureId", 3}, {"bgColor", "0xFFFFFFFF"}}},
|
|
{"hover", {{"textureId", 3}, {"bgColor", "0xFF00FFFF"}}},
|
|
{"pressed", {{"textureId", 3}, {"bgColor", "0x888888FF"}}}
|
|
}}
|
|
}
|
|
}}
|
|
};
|
|
|
|
auto layoutNode = std::make_unique<JsonDataNode>("layout", layoutJson);
|
|
config.setChild("layout", std::move(layoutNode));
|
|
|
|
ui->setConfiguration(config, uiIO.get(), nullptr);
|
|
logger->info("✓ UIModule configured with 3 textured buttons!");
|
|
}
|
|
|
|
logger->info("\n╔════════════════════════════════════════╗");
|
|
logger->info("║ 3 BOUTONS AVEC TEXTURES ║");
|
|
logger->info("╠════════════════════════════════════════╣");
|
|
logger->info("║ Button 1: Car (textureId=1) ║");
|
|
logger->info("║ Button 2: Eyes (textureId=2) ║");
|
|
logger->info("║ Button 3: Icon (textureId=3) ║");
|
|
logger->info("║ Press ESC to exit ║");
|
|
logger->info("╚════════════════════════════════════════╝\n");
|
|
|
|
bool running = true;
|
|
while (running) {
|
|
SDL_Event e;
|
|
while (SDL_PollEvent(&e)) {
|
|
if (e.type == SDL_QUIT ||
|
|
(e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)) {
|
|
running = false;
|
|
}
|
|
|
|
// Forward mouse events
|
|
if (e.type == SDL_MOUSEMOTION) {
|
|
auto mouseMsg = std::make_unique<JsonDataNode>("mouse");
|
|
mouseMsg->setDouble("x", static_cast<double>(e.motion.x));
|
|
mouseMsg->setDouble("y", static_cast<double>(e.motion.y));
|
|
gameIO->publish("input:mouse:move", std::move(mouseMsg));
|
|
}
|
|
else if (e.type == SDL_MOUSEBUTTONDOWN || e.type == SDL_MOUSEBUTTONUP) {
|
|
auto mouseMsg = std::make_unique<JsonDataNode>("mouse");
|
|
mouseMsg->setInt("button", e.button.button);
|
|
mouseMsg->setBool("pressed", e.type == SDL_MOUSEBUTTONDOWN);
|
|
mouseMsg->setDouble("x", static_cast<double>(e.button.x));
|
|
mouseMsg->setDouble("y", static_cast<double>(e.button.y));
|
|
gameIO->publish("input:mouse:button", std::move(mouseMsg));
|
|
}
|
|
}
|
|
|
|
// Check for UI events
|
|
while (gameIO->hasMessages() > 0) {
|
|
auto msg = gameIO->pullMessage();
|
|
if (msg.topic == "ui:action") {
|
|
logger->info("🖱️ BOUTON CLICKÉ!");
|
|
}
|
|
}
|
|
|
|
// Update modules
|
|
JsonDataNode input("input");
|
|
input.setDouble("deltaTime", 0.016);
|
|
ui->process(input);
|
|
renderer->process(input);
|
|
|
|
SDL_Delay(16);
|
|
}
|
|
|
|
logger->info("Cleaning up...");
|
|
|
|
// Textures are managed by ResourceCache, will be cleaned up in renderer->shutdown()
|
|
ui->shutdown();
|
|
renderer->shutdown();
|
|
|
|
IntraIOManager::getInstance().removeInstance("game");
|
|
IntraIOManager::getInstance().removeInstance("ui");
|
|
IntraIOManager::getInstance().removeInstance("renderer");
|
|
|
|
SDL_DestroyWindow(window);
|
|
SDL_Quit();
|
|
|
|
logger->info("Test complete!");
|
|
return 0;
|
|
}
|