I'm glad you asked this question... I spent countless hours figuring this stuff out before I began actually learning C. In regard to the first part, I usually just open up Photoshop and manually place characters over the existing ones... it's tedious but it works.
The UI font drawing functions that you'll generally be concerned with are in ui_atoms.c, UI_DrawProportionalString and its variants. I've gone through and re-written them in my code, but the principle is the same.
Code: Select all
static int font1[128][3] = {
// { start, end, column }
{12, 20, 1}, //!
{40, 56, 1}, //"
{72, 88, 1}, //#
{104, 120, 1}, //$
{136, 152, 1}, //%
{168, 184, 1}, //&
{202, 214, 1}, //'
{234, 246, 1}, //(
{266, 278, 1}, //)
{296, 312, 1}, //*
{328, 344, 1}, //+
{362, 374, 1}, //,
{392, 408, 1}, //-
{428, 436, 1}, //.
{457, 469, 1}, ///
{490, 502, 1}, //0
{12, 20, 2}, //1
{41, 55, 2}, //2
{72, 88, 2}, //3
{104, 120, 2}, //4
{136, 152, 2}, //5
{168, 184, 2}, //6
{200, 216, 2}, //7
{233, 247, 2}, //8
{264, 280, 2}, //9
{298, 310, 2}, //:
{330, 342, 2}, //;
{360, 376, 2}, //<
{392, 408, 2}, //=
{424, 440, 2}, //>
{456, 472, 2}, //?
{486, 506, 2}, //@
{8, 24, 3}, //A
{40, 56, 3}, //B
{72, 88, 3}, //C
{104, 120, 3}, //D
{136, 152, 3}, //E
{168, 182, 3}, //F
{200, 216, 3}, //G
{232, 248, 3}, //H
{266, 276, 3}, //I
{298, 310, 3}, //J
{328, 344, 3}, //K
{360, 374, 3}, //L
{390, 408, 3}, //M
{424, 440, 3}, //N
{456, 472, 3}, //O
{488, 504, 3}, //P
{8, 24, 4}, //Q
{40, 56, 4}, //R
{72, 88, 4}, //S
{104, 118, 4}, //T
{136, 152, 4}, //U
{168, 184, 4}, //V
{198, 218, 4}, //W
{232, 248, 4}, //X
{264, 280, 4}, //Y
{296, 312, 4}, //Z
{332, 340, 4}, //[
{360, 376, 4}, //'\'
{396, 404, 4}, //]
{428, 436, 4}, //^
{456, 472, 4}, //_
{494, 498, 4}, //`
{10, 20, 5}, //a
{43, 54, 5}, //b
{74, 86, 5}, //c
{106, 118, 5}, //d
{140, 148, 5}, //e
{170, 182, 5}, //f
{202, 214, 5}, //g
{234, 246, 5}, //h
{270, 274, 5}, //i
{298, 310, 5}, //j
{330, 342, 5}, //k
{364, 372, 5}, //l
{394, 406, 5}, //m
{426, 438, 5}, //n
{458, 470, 5}, //o
{490, 502, 5}, //p
{10, 22, 6}, //q
{42, 54, 6}, //r
{74, 86, 6}, //s
{107, 116, 6}, //t
{138, 150, 6}, //u
{170, 182, 6}, //v
{200, 216, 6}, //w
{234, 246, 6}, //x
{268, 277, 6}, //y
{300, 308, 6}, //z
{328, 342, 6}, //{
{366, 370, 6}, //|
{392, 406, 6}, //}
{422, 442, 6}, //~
};
static void UI_DrawFont_Ch(int x, int y, const char* str, vec4_t color, float scale, int style)
{
const char* s;
unsigned char ch;
float ax;
float ay;
float w;
float s1;
float s2;
int row;
int len;
UI_SetColor(color);
//Adjust to 640x480
ax = x * uis.scalex;
ay = y * uis.scaley;
len = UI_Font_Width(str);
switch (style & UI_FORMATMASK)
{
case UI_CENTER:
//Center justify at x
ax = ax - (len*scale*uis.scalex/2);
break;
case UI_RIGHT:
//Right justify at x
ax = ax - (len*scale*uis.scalex);
break;
default:
//Left justify at x
break;
}
s = str;
while (*s)
{
//Remove extended ASCII characters
ch = *s & 127;
if (ch == ' ')
ax += 10*scale*uis.scalex;
else
{
ch -= '!';
//ch[][0] = starting x
//ch[][1] = end x
//ch[][2] = row
w = (font1[ch][1] - font1[ch][0]) * uis.scalex;
s1 = (float)font1[ch][0] / 512;
s2 = (float)font1[ch][1] / 512;
row = font1[ch][2];
trap_R_DrawStretchPic( ax, //x coord
ay, //y coord
w*scale, //Width
FONTHEIGHT*scale*uis.scaley, //Height
s1,
((row*.0625)-0.0508),
s2,
((row*.0625)-0.0117),
uis.fontShader ); //Graphic to draw with
ax += w*scale;
}
s++;
}
trap_R_SetColor(NULL);
}
font1 is a two-dimensional array which stores three values for each character, and is ordered according to the ASCII table. It stores beginning and ending X values, as well as which row the character appears on in the font graphic (an image similar to bigchars.tga). The while loop inside of DrawFont then iterates through each character in the string that it's passed, and uses the corresponding coordinates in font1 to calculate what values to give trap_R_DrawStretchPic, which draws the specific part of the image onto a small rectangle. If you turn on r_showtris, you can see it more clearly.
Hopefully that's mostly relevant to what you were asking, the UI is my favorite part of q3 because I actually comprehend most of it