/* Copyright (C) 2012 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "TextRenderer.h" #include "lib/ogl.h" #include "lib/res/graphics/unifont.h" #include "ps/Font.h" extern int g_xres, g_yres; CTextRenderer::CTextRenderer(const CShaderProgramPtr& shader) : m_Shader(shader) { ResetTransform(); Color(CColor(1.0f, 1.0f, 1.0f, 1.0f)); Font(L"sans-10"); } void CTextRenderer::ResetTransform() { m_Transform.SetIdentity(); m_Transform.Scale(1.0f, -1.f, 1.0f); m_Transform.Translate(0.0f, (float)g_yres, -1000.0f); CMatrix3D proj; proj.SetOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f); m_Transform = proj * m_Transform; } CMatrix3D CTextRenderer::GetTransform() { return m_Transform; } void CTextRenderer::SetTransform(const CMatrix3D& transform) { m_Transform = transform; } void CTextRenderer::Translate(float x, float y, float z) { CMatrix3D m; m.SetTranslation(x, y, z); m_Transform = m_Transform * m; } void CTextRenderer::Color(const CColor& color) { m_Color = color; } void CTextRenderer::Color(float r, float g, float b, float a) { m_Color = CColor(r, g, b, a); } void CTextRenderer::Font(const CStrW& font) { if (!m_Fonts[font]) m_Fonts[font] = shared_ptr(new CFont(font)); m_Font = m_Fonts[font]; } void CTextRenderer::PrintfAdvance(const wchar_t* fmt, ...) { wchar_t buf[1024] = {0}; va_list args; va_start(args, fmt); int ret = vswprintf(buf, ARRAY_SIZE(buf)-1, fmt, args); va_end(args); if (ret < 0) debug_printf(L"CTextRenderer::Printf vswprintf failed (buffer size exceeded?) - return value %d, errno %d\n", ret, errno); PutAdvance(buf); } void CTextRenderer::PrintfAt(float x, float y, const wchar_t* fmt, ...) { wchar_t buf[1024] = {0}; va_list args; va_start(args, fmt); int ret = vswprintf(buf, ARRAY_SIZE(buf)-1, fmt, args); va_end(args); if (ret < 0) debug_printf(L"CTextRenderer::PrintfAt vswprintf failed (buffer size exceeded?) - return value %d, errno %d\n", ret, errno); Put(x, y, buf); } void CTextRenderer::PutAdvance(const wchar_t* buf) { Put(0.0f, 0.0f, buf); int w, h; m_Font->CalculateStringSize(buf, w, h); Translate((float)w, 0.0f, 0.0f); } void CTextRenderer::Put(float x, float y, const wchar_t* buf) { if (buf[0] == 0) return; // empty string; don't bother storing CMatrix3D translate; translate.SetTranslation(x, y, 0.0f); SBatch batch; batch.transform = m_Transform * translate; batch.color = m_Color; batch.font = m_Font; batch.text = buf; m_Batches.push_back(batch); } struct t2f_v2i { t2f_v2i() : u(0), v(0), x(0), y(0) { } float u, v; i16 x, y; }; void CTextRenderer::Render() { std::vector indexes; std::vector vertexes; for (size_t i = 0; i < m_Batches.size(); ++i) { SBatch& batch = m_Batches[i]; if (batch.text.empty()) // avoid zero-length arrays continue; const std::map& glyphs = batch.font->GetGlyphs(); m_Shader->BindTexture("tex", batch.font->GetTexture()); m_Shader->Uniform("transform", batch.transform); // ALPHA-only textures will have .rgb sampled as 0, so we need to // replace it with white (but not affect RGBA textures) if (batch.font->HasRGB()) m_Shader->Uniform("colorAdd", CColor(0.0f, 0.0f, 0.0f, 0.0f)); else m_Shader->Uniform("colorAdd", CColor(1.0f, 1.0f, 1.0f, 0.0f)); m_Shader->Uniform("colorMul", batch.color); vertexes.clear(); vertexes.resize(batch.text.size()*4); indexes.clear(); indexes.resize(batch.text.size()*6); i16 x = 0; for (size_t i = 0; i < batch.text.size(); ++i) { std::map::const_iterator it = glyphs.find(batch.text[i]); if (it == glyphs.end()) it = glyphs.find(0xFFFD); // Use the missing glyph symbol if (it == glyphs.end()) // Missing the missing glyph symbol - give up continue; const UnifontGlyphData& g = it->second; vertexes[i*4].u = g.u1; vertexes[i*4].v = g.v0; vertexes[i*4].x = g.x1 + x; vertexes[i*4].y = g.y0; vertexes[i*4+1].u = g.u0; vertexes[i*4+1].v = g.v0; vertexes[i*4+1].x = g.x0 + x; vertexes[i*4+1].y = g.y0; vertexes[i*4+2].u = g.u0; vertexes[i*4+2].v = g.v1; vertexes[i*4+2].x = g.x0 + x; vertexes[i*4+2].y = g.y1; vertexes[i*4+3].u = g.u1; vertexes[i*4+3].v = g.v1; vertexes[i*4+3].x = g.x1 + x; vertexes[i*4+3].y = g.y1; indexes[i*6+0] = i*4+0; indexes[i*6+1] = i*4+1; indexes[i*6+2] = i*4+2; indexes[i*6+3] = i*4+2; indexes[i*6+4] = i*4+3; indexes[i*6+5] = i*4+0; x += g.xadvance; } m_Shader->VertexPointer(2, GL_SHORT, sizeof(t2f_v2i), &vertexes[0].x); m_Shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, sizeof(t2f_v2i), &vertexes[0].u); glDrawElements(GL_TRIANGLES, indexes.size(), GL_UNSIGNED_SHORT, &indexes[0]); } m_Batches.clear(); }