diff --git a/source/lib/res/res.h b/source/lib/res/res.h index bc97f49e1d..7247133751 100755 --- a/source/lib/res/res.h +++ b/source/lib/res/res.h @@ -3,6 +3,7 @@ #include "res/tex.h" #include "res/mem.h" #include "res/font.h" +#include "res/unifont.h" extern int res_reload(const char* fn); diff --git a/source/lib/res/unifont.cpp b/source/lib/res/unifont.cpp new file mode 100755 index 0000000000..7bb7945451 --- /dev/null +++ b/source/lib/res/unifont.cpp @@ -0,0 +1,184 @@ +/* +$Id: unifont.cpp,v 1.1 2004/06/16 15:36:28 philip Exp $ + +Unicode OpenGL texture font + + -- Philip Taylor (philip@wildfiregames.com / philip@zaynar.demon.co.uk) + (based on Jan Wassenberg's font.cpp/.h) +*/ + +#include "precompiled.h" + +#include "lib.h" +#include "res.h" +#include "ogl.h" + +#include +#include +#include + +#include + +// This isn't particularly efficient - it can be improved if we +// (a) care enough, and (b) know about fixed ranges of characters +// that the fonts usually contain +typedef std::map glyphmap; + +glyphmap* BoundGlyphs = NULL; + +struct UniFont +{ + Handle ht; // handle to font texture + glyphmap* glyphs; + uint ListBase; + int LineSpacing; +}; + +H_TYPE_DEFINE(UniFont); + +static void UniFont_init(UniFont* f, va_list args) +{ + f->glyphs = new glyphmap; +} + +static void UniFont_dtor(UniFont* f) +{ + tex_free(f->ht); + glDeleteLists(f->ListBase, (GLsizei)f->glyphs->size()); + f->glyphs->clear(); + delete f->glyphs; +} + + +static int UniFont_reload(UniFont* f, const char* fn) +{ + // fn is the base filename, like "fonts/console" + // The font definition file is fn+".fnt" and the texture is fn+".tga" + + std::string FilenameBase = fn; + + void* RawFNT; + size_t FNTSize; + Handle err = vfs_load((FilenameBase+".fnt").c_str(), RawFNT, FNTSize); + + if (err <= 0) + return (int)err; + + // Get the data in a nicer object + std::istrstream FNTStream ((char*)RawFNT, (int)FNTSize); + + int Version; + FNTStream >> Version; + assert (Version == 100); // Make sure this is from a recent version of the font builder + + int TextureWidth, TextureHeight; + FNTStream >> TextureWidth >> TextureHeight; + + int NumGlyphs; + FNTStream >> NumGlyphs; + + FNTStream >> f->LineSpacing; + + f->ListBase = glGenLists(NumGlyphs); + assert(f->ListBase != 0); // My Voodoo2 drivers didn't support display lists + + for (int i = 0; i < NumGlyphs; ++i) + { + int Codepoint, TextureX, TextureY, Width, Height, OffsetX, OffsetY, Advance; + FNTStream >> Codepoint >> TextureX >> TextureY >> Width >> Height >> OffsetX >> OffsetY >> Advance; + + GLfloat u = (GLfloat)TextureX / (GLfloat)TextureWidth; + GLfloat v = (GLfloat)TextureY / (GLfloat)TextureHeight; + GLfloat w = (GLfloat)Width / (GLfloat)TextureWidth; + GLfloat h = (GLfloat)Height / (GLfloat)TextureHeight; + + glNewList(f->ListBase+i, GL_COMPILE); + glBegin(GL_QUADS); + glTexCoord2f(u, v); glVertex2i(OffsetX, OffsetY); + glTexCoord2f(u+w, v); glVertex2i(OffsetX+Width, OffsetY); + glTexCoord2f(u+w, v-h); glVertex2i(OffsetX+Width, OffsetY-Height); + glTexCoord2f(u, v-h); glVertex2i(OffsetX, OffsetY-Height); + glEnd(); + glTranslatef((GLfloat)Advance, 0, 0); + glEndList(); + + (*f->glyphs)[(wchar_t)Codepoint] = i; + } + + // Load glyph texture + const Handle ht = tex_load((FilenameBase+".tga").c_str()); + if (ht <= 0) + return (int)ht; + + tex_upload(ht, GL_NEAREST, GL_ALPHA8, GL_ALPHA); + + f->ht = ht; + + return 0; +} + + +Handle unifont_load(const char* fn, int scope) +{ + return h_alloc(H_UniFont, fn, scope); +} + + +int unifont_bind(const Handle h) +{ + H_DEREF(h, UniFont, f); + + tex_bind(f->ht); + glListBase(f->ListBase); + BoundGlyphs = f->glyphs; + + return 0; +} + + +int unifont_linespacing(const Handle h) +{ + H_DEREF(h, UniFont, f); + return f->LineSpacing; +} + + +void glwprintf(const wchar_t* fmt, ...) +{ + va_list args; + const int buf_size = 1024; + wchar_t buf[buf_size]; + + va_start(args, fmt); + if (vsnwprintf(buf, buf_size-1, fmt, args) < 0) + debug_out("glwprintf failed (buffer size exceeded?)"); + va_end(args); + + // Make sure there's always NULL termination + buf[buf_size-1] = 0; + + assert(BoundGlyphs != NULL); // You always need to bind something first + + // Count the number of characters + size_t len = wcslen(buf); + + // Replace every wchar_t with the display list offset for the appropriate glyph + for (size_t i = 0; i < len; ++i) + { + glyphmap::iterator it = BoundGlyphs->find(buf[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 + { + debug_out("Missing the missing glyph in a unifont!"); + return; + } + + buf[i] = (*it).second; // Replace with the display list offset + } + + // Execute all the display lists + glCallLists((GLsizei)len, GL_UNSIGNED_SHORT, buf); +} diff --git a/source/lib/res/unifont.h b/source/lib/res/unifont.h new file mode 100755 index 0000000000..7f286b502f --- /dev/null +++ b/source/lib/res/unifont.h @@ -0,0 +1,37 @@ +// $Id: unifont.h,v 1.1 2004/06/16 15:36:28 philip Exp $ + +#ifndef __UNIFONT_H__ +#define __UNIFONT_H__ + +//#include "types.h" +#include "h_mgr.h" + + +// Load and return a handle to the font defined +// in fn+".fnt" with texture fn+".tga" +extern Handle unifont_load(const char* fn, int scope = RES_STATIC); + +// Use the font referenced by h for all subsequent glwprintf() calls. +// Must be called before any glwprintf(). +extern int unifont_bind(Handle h); + +// Output text at current OpenGL modelview pos. +extern void glwprintf(const wchar_t* fmt, ...); + +/* + glwprintf assumes an environment rougly 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); +*/ + +// Return spacing in pixels from one line of text to the next +int unifont_linespacing(const Handle h); + +#endif // __UNIFONT_H__