Add mesh caching for unchanging text labels
This commit is contained in:
parent
4eda57b4e4
commit
b56fafa0e0
23
src/Game.cpp
23
src/Game.cpp
@ -208,7 +208,9 @@ namespace ZL
|
||||
// Можно делать масштаб по дальности: чем дальше — тем меньше.
|
||||
// depth в NDC: ближе к -1 (near) и к 1 (far). Стабильнее считать по расстоянию:
|
||||
float dist = (Environment::shipState.position - boxWorld).norm();
|
||||
float scale = std::clamp(120.0f / (dist + 1.0f), 0.6f, 1.2f);
|
||||
float scaleRaw = 120.0f / (dist + 1.0f);
|
||||
float scale = std::round(scaleRaw * 10.0f) / 10.0f;
|
||||
scale = std::clamp(scale, 0.6f, 1.2f);
|
||||
|
||||
textRenderer->drawText(boxLabels[i], uiX, uiY, scale, /*centered*/true);
|
||||
}
|
||||
@ -465,9 +467,6 @@ namespace ZL
|
||||
|
||||
boxAlive.resize(boxCoordsArr.size(), true);
|
||||
ZL::CheckGlError();
|
||||
textRenderer = std::make_unique<ZL::TextRenderer>();
|
||||
textRenderer->init(renderer, "resources/fonts/DroidSans.ttf", 32, CONST_ZIP_FILE);
|
||||
ZL::CheckGlError();
|
||||
boxLabels.clear();
|
||||
boxLabels.reserve(boxCoordsArr.size());
|
||||
for (size_t i = 0; i < boxCoordsArr.size(); ++i) {
|
||||
@ -480,6 +479,14 @@ namespace ZL
|
||||
}
|
||||
|
||||
renderer.InitOpenGL();
|
||||
|
||||
// TextRenderer создаём/инициализируем ПОСЛЕ инициализации OpenGL
|
||||
textRenderer = std::make_unique<ZL::TextRenderer>();
|
||||
if (!textRenderer->init(renderer, "resources/fonts/DroidSans.ttf", 32, CONST_ZIP_FILE)) {
|
||||
std::cerr << "Failed to init TextRenderer\n";
|
||||
}
|
||||
ZL::CheckGlError();
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
@ -1316,6 +1323,14 @@ namespace ZL
|
||||
if (event.type == SDL_QUIT) {
|
||||
Environment::exitGameLoop = true;
|
||||
}
|
||||
#if SDL_VERSION_ATLEAST(2,0,5)
|
||||
else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
// Обновляем размеры и сбрасываем кеш текстов, т.к. меши хранятся в пикселях
|
||||
Environment::width = event.window.data1;
|
||||
Environment::height = event.window.data2;
|
||||
if (textRenderer) textRenderer->ClearCache();
|
||||
}
|
||||
#endif
|
||||
#ifdef __ANDROID__
|
||||
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) {
|
||||
Environment::exitGameLoop = true;
|
||||
|
||||
@ -62,6 +62,11 @@ bool TextRenderer::init(Renderer& renderer, const std::string& ttfPath, int pixe
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextRenderer::ClearCache()
|
||||
{
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
bool TextRenderer::loadGlyphs(const std::string& ttfPath, int pixelSize, const std::string& zipfilename)
|
||||
{
|
||||
// 1. Загружаем сырые данные из ZIP
|
||||
@ -296,27 +301,22 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
|
||||
{
|
||||
if (!r || text.empty() || !atlasTexture) return;
|
||||
|
||||
// 1. Считаем ширину для центрирования
|
||||
float totalW = 0.0f;
|
||||
if (centered) {
|
||||
for (char ch : text) {
|
||||
auto it = glyphs.find(ch);
|
||||
if (it == glyphs.end()) continue;
|
||||
totalW += (it->second.advance >> 6) * scale;
|
||||
}
|
||||
x -= totalW * 0.5f;
|
||||
}
|
||||
// формируем ключ кеша
|
||||
std::string key = text + "|" + std::to_string(scale) + "|" + (centered ? "1" : "0");
|
||||
auto itCache = cache.find(key);
|
||||
|
||||
// 2. Подготовка данных (аналог CreateRect2D, но для всей строки)
|
||||
if (itCache == cache.end()) {
|
||||
VertexDataStruct textData;
|
||||
float penX = x;
|
||||
float penY = y;
|
||||
float penX = 0.0f;
|
||||
float penY = 0.0f;
|
||||
|
||||
float totalW = 0.0f;
|
||||
float maxH = 0.0f;
|
||||
|
||||
for (char ch : text) {
|
||||
auto it = glyphs.find(ch);
|
||||
if (it == glyphs.end()) continue;
|
||||
|
||||
const GlyphInfo& g = it->second;
|
||||
auto git = glyphs.find(ch);
|
||||
if (git == glyphs.end()) continue;
|
||||
const GlyphInfo& g = git->second;
|
||||
|
||||
float xpos = penX + g.bearing.x() * scale;
|
||||
float ypos = penY - (g.size.y() - g.bearing.y()) * scale;
|
||||
@ -324,7 +324,6 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
|
||||
float h = g.size.y() * scale;
|
||||
|
||||
// Добавляем 2 треугольника (6 вершин) для текущего символа
|
||||
// Координаты Z ставим 0.0f, так как это 2D
|
||||
textData.PositionData.push_back({ xpos, ypos + h, 0.0f });
|
||||
textData.PositionData.push_back({ xpos, ypos, 0.0f });
|
||||
textData.PositionData.push_back({ xpos + w, ypos, 0.0f });
|
||||
@ -338,15 +337,6 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
|
||||
float u1 = u0 + g.uvSize.x();
|
||||
float v1 = v0 + g.uvSize.y();
|
||||
|
||||
//// UV-координаты (здесь есть нюанс с атласом, ниже поясню)
|
||||
//textData.TexCoordData.push_back({ 0.0f, 0.0f });
|
||||
//textData.TexCoordData.push_back({ 0.0f, 1.0f });
|
||||
//textData.TexCoordData.push_back({ 1.0f, 1.0f });
|
||||
//textData.TexCoordData.push_back({ 0.0f, 0.0f });
|
||||
//textData.TexCoordData.push_back({ 1.0f, 1.0f });
|
||||
//textData.TexCoordData.push_back({ 1.0f, 0.0f });
|
||||
|
||||
// Соответствие прежней системе UV: (0,0)=верх-лево, (0,1)=ниж-лево
|
||||
textData.TexCoordData.push_back({ u0, v0 });
|
||||
textData.TexCoordData.push_back({ u0, v1 });
|
||||
textData.TexCoordData.push_back({ u1, v1 });
|
||||
@ -355,13 +345,35 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
|
||||
textData.TexCoordData.push_back({ u1, v0 });
|
||||
|
||||
penX += (g.advance >> 6) * scale;
|
||||
totalW = penX;
|
||||
maxH = max(maxH, h);
|
||||
}
|
||||
|
||||
// Сохраняем в кеш
|
||||
CachedText ct;
|
||||
ct.width = totalW;
|
||||
ct.height = maxH;
|
||||
ct.mesh.AssignFrom(textData);
|
||||
|
||||
auto res = cache.emplace(key, std::move(ct));
|
||||
itCache = res.first;
|
||||
}
|
||||
|
||||
// Используем кешированный меш
|
||||
CachedText& cached = itCache->second;
|
||||
|
||||
// Вычисляем смещение для проекции (оставляем Y как есть)
|
||||
float tx = x;
|
||||
if (centered) {
|
||||
tx = x - cached.width * 0.5f;
|
||||
}
|
||||
float ty = y;
|
||||
|
||||
// 3. Обновляем VBO через наш стандартный механизм
|
||||
// Примечание: для текста лучше использовать GL_DYNAMIC_DRAW,
|
||||
// но RefreshVBO сейчас жестко зашит на GL_STATIC_DRAW.
|
||||
// Для UI это обычно не критично, если строк не тысячи.
|
||||
textMesh.AssignFrom(textData);
|
||||
// textMesh.AssignFrom(textData);
|
||||
|
||||
// 4. Рендеринг
|
||||
r->shaderManager.PushShader(shaderName);
|
||||
@ -372,8 +384,9 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
|
||||
Eigen::Matrix4f proj = Eigen::Matrix4f::Identity();
|
||||
proj(0, 0) = 2.0f / W;
|
||||
proj(1, 1) = 2.0f / H;
|
||||
proj(0, 3) = -1.0f;
|
||||
proj(1, 3) = -1.0f;
|
||||
// Сдвигаем проекцию так, чтобы локальные координаты меша (pen-origin=0,0) оказались в (tx,ty)
|
||||
proj(0, 3) = -1.0f + 2.0f * (tx) / W;
|
||||
proj(1, 3) = -1.0f + 2.0f * (ty) / H;
|
||||
|
||||
r->RenderUniformMatrix4fv("uProjection", false, proj.data());
|
||||
r->RenderUniform1i("uText", 0);
|
||||
@ -402,7 +415,7 @@ void TextRenderer::drawText(const std::string& text, float x, float y, float sca
|
||||
|
||||
// glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
//}
|
||||
r->DrawVertexRenderStruct(textMesh);
|
||||
r->DrawVertexRenderStruct(cached.mesh);
|
||||
|
||||
r->DisableVertexAttribArray("vPosition");
|
||||
r->DisableVertexAttribArray("vTexCoord");
|
||||
|
||||
@ -29,6 +29,9 @@ public:
|
||||
bool init(Renderer& renderer, const std::string& ttfPath, int pixelSize, const std::string& zipfilename);
|
||||
void drawText(const std::string& text, float x, float y, float scale, bool centered, std::array<float, 4> color = { 1.f,1.f,1.f,1.f });
|
||||
|
||||
// Clear cached meshes (call on window resize / DPI change)
|
||||
void ClearCache();
|
||||
|
||||
private:
|
||||
bool loadGlyphs(const std::string& ttfPath, int pixelSize, const std::string& zipfilename);
|
||||
|
||||
@ -48,6 +51,16 @@ private:
|
||||
VertexRenderStruct textMesh;
|
||||
|
||||
std::string shaderName = "text2d";
|
||||
|
||||
// caching for static texts
|
||||
struct CachedText {
|
||||
VertexRenderStruct mesh;
|
||||
float width = 0.f; // in pixels, total advance
|
||||
float height = 0.f; // optional, not currently used
|
||||
};
|
||||
|
||||
// key: text + "|" + scale + "|" + centered
|
||||
std::unordered_map<std::string, CachedText> cache;
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
Loading…
Reference in New Issue
Block a user