From 832a51da8078c645380d766d060222b648848a96 Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Wed, 15 Feb 2012 21:53:13 +0000 Subject: [PATCH] GLSL text support This was SVN commit r11080. --- .../data/mods/public/shaders/glsl/gui_text.vs | 7 +- .../mods/public/shaders/glsl/gui_text.xml | 2 + source/graphics/TextRenderer.cpp | 77 +++++++- source/gui/CInput.cpp | 1 - source/lib/res/graphics/unifont.cpp | 173 +++--------------- source/lib/res/graphics/unifont.h | 58 +++--- source/ps/CConsole.cpp | 1 - source/ps/Font.cpp | 15 +- source/ps/Font.h | 16 +- source/ps/ProfileViewer.cpp | 1 - 10 files changed, 143 insertions(+), 208 deletions(-) diff --git a/binaries/data/mods/public/shaders/glsl/gui_text.vs b/binaries/data/mods/public/shaders/glsl/gui_text.vs index c6f772fff0..6924eb2082 100644 --- a/binaries/data/mods/public/shaders/glsl/gui_text.vs +++ b/binaries/data/mods/public/shaders/glsl/gui_text.vs @@ -4,8 +4,11 @@ uniform mat4 transform; varying vec2 v_texcoord; +attribute vec3 a_vertex; +attribute vec2 a_uv0; + void main() { - gl_Position = transform * gl_Vertex; - v_texcoord = gl_MultiTexCoord0.xy; + gl_Position = transform * vec4(a_vertex, 1.0); + v_texcoord = a_uv0; } diff --git a/binaries/data/mods/public/shaders/glsl/gui_text.xml b/binaries/data/mods/public/shaders/glsl/gui_text.xml index 145c38c428..3590081f9c 100644 --- a/binaries/data/mods/public/shaders/glsl/gui_text.xml +++ b/binaries/data/mods/public/shaders/glsl/gui_text.xml @@ -4,6 +4,8 @@ + + diff --git a/source/graphics/TextRenderer.cpp b/source/graphics/TextRenderer.cpp index 700577717b..f1a97eb6a8 100644 --- a/source/graphics/TextRenderer.cpp +++ b/source/graphics/TextRenderer.cpp @@ -91,6 +91,9 @@ void CTextRenderer::Printf(const wchar_t* fmt, ...) if (ret < 0) debug_printf(L"CTextRenderer::Printf vswprintf failed (buffer size exceeded?) - return value %d, errno %d\n", ret, errno); + if (ret == 0) + return; // empty string; don't bother storing + SBatch batch; batch.transform = m_Transform; batch.color = m_Color; @@ -115,6 +118,9 @@ void CTextRenderer::PrintfAt(float x, float y, const wchar_t* fmt, ...) if (ret < 0) debug_printf(L"CTextRenderer::PrintfAt vswprintf failed (buffer size exceeded?) - return value %d, errno %d\n", ret, errno); + if (ret == 0) + return; // empty string; don't bother storing + CMatrix3D translate; translate.SetTranslation(x, y, 0.0f); @@ -126,17 +132,28 @@ void CTextRenderer::PrintfAt(float x, float y, const wchar_t* fmt, ...) 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]; - int unit = m_Shader->GetTextureUnit("tex"); - if (unit == -1) // just in case the shader doesn't use the sampler + if (batch.text.empty()) // avoid zero-length arrays continue; - batch.font->Bind(unit); + const std::map& glyphs = batch.font->GetGlyphs(); + + m_Shader->BindTexture("tex", batch.font->GetTexture()); m_Shader->Uniform("transform", batch.transform); @@ -149,7 +166,59 @@ void CTextRenderer::Render() m_Shader->Uniform("colorMul", batch.color); - unifont_render(batch.text.c_str()); + 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(); diff --git a/source/gui/CInput.cpp b/source/gui/CInput.cpp index fd49327e53..0b8e5233b4 100644 --- a/source/gui/CInput.cpp +++ b/source/gui/CInput.cpp @@ -1035,7 +1035,6 @@ void CInput::Draw() } CFont font(font_name); - font.Bind(); // We'll have to setup clipping manually, since we're doing the rendering manually. CRect cliparea(m_CachedActualSize); diff --git a/source/lib/res/graphics/unifont.cpp b/source/lib/res/graphics/unifont.cpp index 847d21becd..2c21ddb044 100644 --- a/source/lib/res/graphics/unifont.cpp +++ b/source/lib/res/graphics/unifont.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 Wildfire Games +/* Copyright (c) 2012 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -35,16 +35,7 @@ #include "ogl_tex.h" #include "lib/res/h_mgr.h" -struct GlyphData -{ - float u0, v0, u1, v1; - i16 x0, y0, x1, y1; - i16 xadvance; -}; - -typedef std::map glyphmap; - -static glyphmap* BoundGlyphs = NULL; +typedef std::map glyphmap; struct UniFont { @@ -149,7 +140,7 @@ static Status UniFont_reload(UniFont* f, const PIVFS& vfs, const VfsPath& basena GLfloat w = (GLfloat)Width / (GLfloat)TextureWidth; GLfloat h = (GLfloat)Height / (GLfloat)TextureHeight; - GlyphData g = { u, -v, u+w, -v+h, (i16)OffsetX, (i16)-OffsetY, (i16)(OffsetX+Width), (i16)(-OffsetY+Height), (i16)Advance }; + UnifontGlyphData g = { u, -v, u+w, -v+h, (i16)OffsetX, (i16)-OffsetY, (i16)(OffsetX+Width), (i16)(-OffsetY+Height), (i16)Advance }; (*f->glyphs)[(u16)Codepoint] = g; } @@ -212,27 +203,10 @@ Handle unifont_load(const PIVFS& vfs, const VfsPath& pathname, size_t flags) Status unifont_unload(Handle& h) { H_DEREF(h, UniFont, f); - - // unbind ourself, so people will get errors if - // they draw more text without binding a new font - if (BoundGlyphs == f->glyphs) - BoundGlyphs = NULL; - return h_free(h, H_UniFont); } -Status unifont_bind(const Handle h, size_t unit) -{ - H_DEREF(h, UniFont, f); - - ogl_tex_bind(f->ht, unit); - BoundGlyphs = f->glyphs; - - return INFO::OK; -} - - int unifont_linespacing(const Handle h) { H_DEREF(h, UniFont, f); @@ -267,122 +241,6 @@ int unifont_character_width(const Handle h, wchar_t c) return it->second.xadvance; } - -struct t2f_v2i -{ - float u, v; - i16 x, y; -}; - -void glvwprintf(const wchar_t* fmt, va_list args) -{ - const int buf_size = 1024; - wchar_t buf[buf_size]; - - int ret = vswprintf(buf, buf_size-1, fmt, args); - if(ret < 0) { - debug_printf(L"glwprintf failed (buffer size exceeded?) - return value %d, errno %d\n", ret, errno); - } - - // Make sure there's always null termination - buf[buf_size-1] = 0; - - int advance = 0; - unifont_render(buf, &advance); - - // Move into position for subsequent prints -#if CONFIG2_GLES -#warning TODO: implement unifont for GLES -#else - glTranslatef((float)advance, 0, 0); -#endif -} - -void unifont_render(const wchar_t* str, int* advance) -{ - ENSURE(BoundGlyphs != NULL); // You always need to bind something first - - // Count the number of characters - size_t len = wcslen(str); - - // 0 glyphs -> nothing to do (avoid BoundsChecker warning) - if (!len) - return; - - t2f_v2i* vertexes = new t2f_v2i[len*4]; - - i16 x = 0; - - for (size_t i = 0; i < len; ++i) - { - glyphmap::iterator it = BoundGlyphs->find(str[i]); - - if (it == BoundGlyphs->end()) - it = BoundGlyphs->find(0xFFFD); // Use the missing glyph symbol - - if (it == BoundGlyphs->end()) // Missing the missing glyph symbol - give up - continue; - - const GlyphData& 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; - - x += g.xadvance; - } - - ogl_WarnIfError(); - -#if CONFIG2_GLES -#warning TODO: implement unifont for GLES -#else - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - glVertexPointer(2, GL_SHORT, sizeof(t2f_v2i), (u8*)vertexes + offsetof(t2f_v2i, x)); - glTexCoordPointer(2, GL_FLOAT, sizeof(t2f_v2i), (u8*)vertexes + offsetof(t2f_v2i, u)); - - glDrawArrays(GL_QUADS, 0, (GLsizei)len*4); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - ogl_WarnIfError(); -#endif - - if (advance) - *advance = x; - - delete[] vertexes; -} - - -void glwprintf(const wchar_t* fmt, ...) -{ - va_list args; - va_start(args, fmt); - glvwprintf(fmt, args); - va_end(args); -} - - Status unifont_stringsize(const Handle h, const wchar_t* text, int& width, int& height) { H_DEREF(h, UniFont, f); @@ -410,3 +268,28 @@ Status unifont_stringsize(const Handle h, const wchar_t* text, int& width, int& return INFO::OK; } + +const glyphmap& unifont_get_glyphs(const Handle h) +{ + UniFont* const f = H_USER_DATA(h, UniFont); + if(!f) + { + DEBUG_WARN_ERR(ERR::INVALID_HANDLE); + static glyphmap dummy; + return dummy; + } + + return *f->glyphs; +} + +Handle unifont_get_texture(const Handle h) +{ + UniFont* const f = H_USER_DATA(h, UniFont); + if(!f) + { + DEBUG_WARN_ERR(ERR::INVALID_HANDLE); + return 0; + } + + return f->ht; +} diff --git a/source/lib/res/graphics/unifont.h b/source/lib/res/graphics/unifont.h index bd8fd6740b..0c7b5adacc 100644 --- a/source/lib/res/graphics/unifont.h +++ b/source/lib/res/graphics/unifont.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 Wildfire Games +/* Copyright (c) 2012 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -48,42 +48,6 @@ extern Handle unifont_load(const PIVFS& vfs, const VfsPath& pathname, size_t fla **/ extern Status unifont_unload(Handle& h); -/** - * Use a font for all subsequent glwprintf() calls. - * - * Must be called before any glwprintf(). - **/ -extern Status unifont_bind(Handle h, size_t unit); - -/** - * Output text at current OpenGL modelview pos. - * - * @param fmt - see fprintf - * - * this assumes an environment roughly like: - * glEnable(GL_TEXTURE_2D); - * glDisable(GL_CULL_FACE); - * glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - * glEnable(GL_BLEND); - * glDisable(GL_ALPHA_TEST); - * glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - **/ -extern void glwprintf(const wchar_t* fmt, ...) WPRINTF_ARGS(1); - -/** - * Varargs version of glwprintf. - * - * @param fmt - * @param args - * @see vfprintf - **/ -extern void glvwprintf(const wchar_t* fmt, va_list args) VWPRINTF_ARGS(1); - -/** - * Output text, and return advance distance (if @p advance not NULL). - */ -extern void unifont_render(const wchar_t* str, int* advance = NULL); - /** * Determine pixel extents of a string. * @@ -117,4 +81,24 @@ int unifont_character_width(const Handle h, wchar_t c); **/ int unifont_linespacing(const Handle h); +// Raw access to font data (since it's convenient to move as much of the +// processing as possible to outside lib/): + +struct UnifontGlyphData +{ + float u0, v0, u1, v1; + i16 x0, y0, x1, y1; + i16 xadvance; +}; + +/** + * @return glyph data for all glyphs in this font. + */ +const std::map& unifont_get_glyphs(const Handle h); + +/** + * @return texture handle for this font. + */ +Handle unifont_get_texture(const Handle h); + #endif // INCLUDED_UNIFONT diff --git a/source/ps/CConsole.cpp b/source/ps/CConsole.cpp index fe009f5509..742a2423fb 100644 --- a/source/ps/CConsole.cpp +++ b/source/ps/CConsole.cpp @@ -180,7 +180,6 @@ void CConsole::Render() PROFILE3_GPU("console"); CFont font(CONSOLE_FONT); - font.Bind(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); diff --git a/source/ps/Font.cpp b/source/ps/Font.cpp index 0a15fe9b54..a2bc4a0499 100644 --- a/source/ps/Font.cpp +++ b/source/ps/Font.cpp @@ -48,11 +48,6 @@ CFont::~CFont() unifont_unload(h); } -void CFont::Bind(size_t unit) -{ - unifont_bind(h, unit); -} - bool CFont::HasRGB() { return unifont_has_rgb(h); @@ -77,3 +72,13 @@ void CFont::CalculateStringSize(const CStrW& string, int& width, int& height) { unifont_stringsize(h, string.c_str(), width, height); } + +const std::map& CFont::GetGlyphs() +{ + return unifont_get_glyphs(h); +} + +Handle CFont::GetTexture() +{ + return unifont_get_texture(h); +} \ No newline at end of file diff --git a/source/ps/Font.h b/source/ps/Font.h index 016cfc92ff..67d689e1d9 100644 --- a/source/ps/Font.h +++ b/source/ps/Font.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* 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 @@ -21,16 +21,7 @@ #include "lib/res/handle.h" class CStrW; - -/* - -To use CFont: - -CFont font("name"); -font.Bind(); -glwprintf(L"Hello world"); - -*/ +struct UnifontGlyphData; class CFont { @@ -38,12 +29,13 @@ public: CFont(const CStrW& name); ~CFont(); - void Bind(size_t unit = 0); bool HasRGB(); int GetLineSpacing(); int GetHeight(); int GetCharacterWidth(wchar_t c); void CalculateStringSize(const CStrW& string, int& w, int& h); + const std::map& GetGlyphs(); + Handle GetTexture(); private: Handle h; diff --git a/source/ps/ProfileViewer.cpp b/source/ps/ProfileViewer.cpp index 69c6e93f1c..f9e95ccea2 100644 --- a/source/ps/ProfileViewer.cpp +++ b/source/ps/ProfileViewer.cpp @@ -173,7 +173,6 @@ void CProfileViewer::RenderProfile() CStrW font_name = L"mono-stroke-10"; CFont font(font_name); - font.Bind(); int lineSpacing = font.GetLineSpacing(); // Render background