2026-02-17 19:24:50 +08:00
|
|
|
/**
|
|
|
|
|
* @file main.cpp
|
|
|
|
|
* @brief Extra2D 文字渲染示例
|
|
|
|
|
*
|
2026-02-20 00:12:19 +08:00
|
|
|
* 演示如何使用 RenderModule 渲染文字
|
2026-02-17 19:24:50 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <extra2d/extra2d.h>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
|
|
using namespace extra2d;
|
|
|
|
|
|
|
|
|
|
class TextRenderingScene : public Scene {
|
|
|
|
|
public:
|
|
|
|
|
void onEnter() override {
|
2026-02-20 00:12:19 +08:00
|
|
|
auto &fontManager = FontManager::getInstance();
|
|
|
|
|
auto *renderMod = Application::get().get<RenderModule>();
|
|
|
|
|
if (!renderMod) {
|
|
|
|
|
std::cerr << "RenderModule not available!" << std::endl;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto *renderer = renderMod->getRenderer();
|
|
|
|
|
if (!renderer) {
|
2026-02-17 20:16:07 +08:00
|
|
|
std::cerr << "Renderer not available!" << std::endl;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-20 00:12:19 +08:00
|
|
|
auto *context = renderer->getContext();
|
|
|
|
|
if (!context) {
|
|
|
|
|
std::cerr << "RenderContext not available!" << std::endl;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!fontManager.isInitialized()) {
|
|
|
|
|
fontManager.init(context->getDevice());
|
2026-02-17 19:24:50 +08:00
|
|
|
}
|
|
|
|
|
|
2026-02-20 00:12:19 +08:00
|
|
|
fontHandle_ = fontManager.load("assets/fonts/fonts.ttf",
|
|
|
|
|
{.fontSize = 26, .useSDF = true});
|
|
|
|
|
if (fontHandle_) {
|
|
|
|
|
fontAtlas_ = fontManager.getAtlas(fontHandle_, 26);
|
|
|
|
|
if (fontAtlas_) {
|
|
|
|
|
std::cout << "Font loaded successfully!" << std::endl;
|
|
|
|
|
std::cout << " Font size: " << fontAtlas_->getFontSize() << std::endl;
|
|
|
|
|
std::cout << " Line height: " << fontAtlas_->getLineHeight()
|
|
|
|
|
<< std::endl;
|
|
|
|
|
std::cout << " Ascent: " << fontAtlas_->getAscent() << std::endl;
|
|
|
|
|
std::cout << " Descent: " << fontAtlas_->getDescent() << std::endl;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
std::cerr << "Failed to load font from assets/fonts/fonts.ttf"
|
|
|
|
|
<< std::endl;
|
2026-02-17 19:24:50 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-20 00:12:19 +08:00
|
|
|
void onExit() override {
|
|
|
|
|
fontAtlas_.reset();
|
|
|
|
|
fontHandle_.reset();
|
|
|
|
|
}
|
2026-02-17 19:24:50 +08:00
|
|
|
|
|
|
|
|
void onRender(RenderBackend &renderer) override {
|
|
|
|
|
Scene::onRender(renderer);
|
|
|
|
|
|
2026-02-20 00:12:19 +08:00
|
|
|
if (!fontAtlas_) {
|
2026-02-17 19:24:50 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float y = 100.0f;
|
|
|
|
|
float x = 100.0f;
|
2026-02-20 00:12:19 +08:00
|
|
|
float fontSize = 26.0f;
|
2026-02-17 19:24:50 +08:00
|
|
|
|
2026-02-20 00:12:19 +08:00
|
|
|
renderText(renderer, "Extra2D Text Rendering Demo", x, y, fontSize,
|
2026-02-17 19:24:50 +08:00
|
|
|
Color(1.0f, 0.8f, 0.2f, 1.0f));
|
2026-02-20 00:12:19 +08:00
|
|
|
y += fontAtlas_->getLineHeight() * 2;
|
2026-02-17 19:24:50 +08:00
|
|
|
|
2026-02-20 00:12:19 +08:00
|
|
|
renderText(renderer, "Red Text", x, y, fontSize,
|
|
|
|
|
Color(1.0f, 0.2f, 0.2f, 1.0f));
|
|
|
|
|
y += fontAtlas_->getLineHeight();
|
2026-02-17 19:24:50 +08:00
|
|
|
|
2026-02-20 00:12:19 +08:00
|
|
|
renderText(renderer, "Green Text", x, y, fontSize,
|
|
|
|
|
Color(0.2f, 1.0f, 0.2f, 1.0f));
|
|
|
|
|
y += fontAtlas_->getLineHeight();
|
2026-02-17 19:24:50 +08:00
|
|
|
|
2026-02-20 00:12:19 +08:00
|
|
|
renderText(renderer, "Blue Text", x, y, fontSize,
|
|
|
|
|
Color(0.2f, 0.2f, 1.0f, 1.0f));
|
|
|
|
|
y += fontAtlas_->getLineHeight() * 2;
|
2026-02-17 19:24:50 +08:00
|
|
|
|
2026-02-20 00:12:19 +08:00
|
|
|
renderText(renderer, "This is a multi-line text example.", x, y, fontSize,
|
2026-02-17 19:24:50 +08:00
|
|
|
Color(1.0f, 1.0f, 1.0f, 1.0f));
|
2026-02-20 00:12:19 +08:00
|
|
|
y += fontAtlas_->getLineHeight();
|
2026-02-17 20:16:07 +08:00
|
|
|
renderText(renderer, "You can render text with different colors", x, y,
|
2026-02-20 00:12:19 +08:00
|
|
|
fontSize, Color(0.8f, 0.8f, 0.8f, 1.0f));
|
|
|
|
|
y += fontAtlas_->getLineHeight();
|
|
|
|
|
renderText(renderer, "and styles using FontAtlas.", x, y, fontSize,
|
2026-02-17 19:24:50 +08:00
|
|
|
Color(0.6f, 0.6f, 0.6f, 1.0f));
|
2026-02-20 00:12:19 +08:00
|
|
|
y += fontAtlas_->getLineHeight() * 2;
|
2026-02-17 19:24:50 +08:00
|
|
|
|
2026-02-20 00:12:19 +08:00
|
|
|
renderText(renderer, "Semi-transparent text (50% opacity)", x, y, fontSize,
|
2026-02-17 19:24:50 +08:00
|
|
|
Color(1.0f, 1.0f, 1.0f, 0.5f));
|
2026-02-20 00:12:19 +08:00
|
|
|
y += fontAtlas_->getLineHeight() * 2;
|
2026-02-17 19:24:50 +08:00
|
|
|
|
2026-02-20 00:12:19 +08:00
|
|
|
renderText(renderer, "Press ESC to exit", x, y, fontSize,
|
2026-02-17 19:24:50 +08:00
|
|
|
Color(0.5f, 0.5f, 0.5f, 1.0f));
|
2026-02-17 20:16:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-02-17 19:24:50 +08:00
|
|
|
private:
|
2026-02-20 00:12:19 +08:00
|
|
|
/**
|
|
|
|
|
* @brief 渲染文本
|
|
|
|
|
*/
|
2026-02-17 20:16:07 +08:00
|
|
|
void renderText(RenderBackend &renderer, const std::string &text, float x,
|
2026-02-20 00:12:19 +08:00
|
|
|
float y, float fontSize, const Color &color) {
|
|
|
|
|
if (!fontAtlas_) {
|
2026-02-17 19:24:50 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-20 00:12:19 +08:00
|
|
|
renderer.drawText(text, Vec2(x, y), fontSize, color);
|
2026-02-17 19:24:50 +08:00
|
|
|
}
|
|
|
|
|
|
2026-02-20 00:12:19 +08:00
|
|
|
FontHandle fontHandle_;
|
|
|
|
|
Ptr<FontAtlas> fontAtlas_;
|
2026-02-17 19:24:50 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
|
|
|
|
|
|
Application &app = Application::get();
|
|
|
|
|
|
|
|
|
|
app.use<WindowModule>([](auto &cfg) {
|
|
|
|
|
cfg.w = 1280;
|
|
|
|
|
cfg.h = 720;
|
2026-02-19 01:29:32 +08:00
|
|
|
cfg.title = "Extra2D 文字显示示例";
|
2026-02-17 19:24:50 +08:00
|
|
|
cfg.priority = 0;
|
2026-02-19 01:29:32 +08:00
|
|
|
cfg.backend = "sdl2";
|
2026-02-17 19:24:50 +08:00
|
|
|
});
|
|
|
|
|
|
2026-02-20 00:12:19 +08:00
|
|
|
app.use<RenderModule>([](auto &cfg) { cfg.api = rhi::GraphicsAPI::OpenGL; });
|
2026-02-17 19:24:50 +08:00
|
|
|
|
|
|
|
|
app.use<InputModule>([](auto &cfg) { cfg.priority = 20; });
|
|
|
|
|
|
|
|
|
|
if (!app.init()) {
|
2026-02-18 17:23:17 +08:00
|
|
|
E2D_LOG_INFO("Failed to initialize application!");
|
2026-02-17 19:24:50 +08:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-18 17:23:17 +08:00
|
|
|
auto win = app.window();
|
2026-02-17 19:24:50 +08:00
|
|
|
if (win) {
|
2026-02-18 17:23:17 +08:00
|
|
|
E2D_LOG_INFO("Window :{} x {}", win->width(), win->height());
|
2026-02-17 19:24:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto eventService = ServiceLocator::instance().getService<IEventService>();
|
|
|
|
|
if (eventService) {
|
|
|
|
|
eventService->addListener(EventType::KeyPressed, [](Event &e) {
|
|
|
|
|
auto &keyEvent = std::get<KeyEvent>(e.data);
|
|
|
|
|
if (keyEvent.keyCode == static_cast<int>(Key::Escape)) {
|
|
|
|
|
e.handled = true;
|
|
|
|
|
Application::get().quit();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto scene = makeShared<TextRenderingScene>();
|
|
|
|
|
scene->setBackgroundColor(Color(0.1f, 0.1f, 0.15f, 1.0f));
|
|
|
|
|
|
|
|
|
|
if (win) {
|
|
|
|
|
scene->setViewportSize(static_cast<float>(win->width()),
|
|
|
|
|
static_cast<float>(win->height()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto cameraService = ServiceLocator::instance().getService<ICameraService>();
|
|
|
|
|
if (cameraService && win) {
|
2026-02-19 01:29:32 +08:00
|
|
|
cameraService->setViewportConfig([&](auto &cfg) {
|
2026-02-20 00:12:19 +08:00
|
|
|
cfg.designWidth = static_cast<float>(win->width());
|
|
|
|
|
cfg.designHeight = static_cast<float>(win->height());
|
|
|
|
|
cfg.scaleMode = ViewportScaleMode::Letterbox;
|
2026-02-19 01:29:32 +08:00
|
|
|
});
|
2026-02-17 19:24:50 +08:00
|
|
|
cameraService->updateViewport(win->width(), win->height());
|
|
|
|
|
cameraService->applyViewportAdapter();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
app.enterScene(scene);
|
|
|
|
|
|
|
|
|
|
app.run();
|
|
|
|
|
|
|
|
|
|
app.shutdown();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|