openmohaa/code/renderer/tr_font.cpp

544 lines
16 KiB
C++
Raw Normal View History

2023-05-08 14:33:37 +02:00
/*
===========================================================================
2023-05-09 19:18:16 +02:00
Copyright (C) 2023 the OpenMoHAA team
2023-05-08 14:33:37 +02:00
2023-05-09 19:18:16 +02:00
This file is part of OpenMoHAA source code.
2023-05-08 14:33:37 +02:00
2023-05-09 19:18:16 +02:00
OpenMoHAA source code is free software; you can redistribute it
2023-05-08 14:33:37 +02:00
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.
2023-05-09 19:18:16 +02:00
OpenMoHAA source code is distributed in the hope that it will be
2023-05-08 14:33:37 +02:00
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
2023-05-09 19:18:16 +02:00
along with OpenMoHAA source code; if not, write to the Free Software
2023-05-08 14:33:37 +02:00
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
2023-05-09 19:18:16 +02:00
// tr_font.cpp -- font rendering
2023-05-08 14:33:37 +02:00
#include "tr_local.h"
2023-05-09 19:13:37 +02:00
#define MAX_LOADED_FONTS 255
static fontheader_t s_loadedFonts[MAX_LOADED_FONTS];
static int s_numLoadedFonts = 0;
static float s_fontHeightScale = 1.0;
static float s_fontGeneralScale = 1.0;
2023-05-10 23:18:45 +02:00
static float s_fontZ = 0.0;
2023-05-08 14:33:37 +02:00
void R_SetFontHeightScale(float scale)
{
2023-05-09 19:13:37 +02:00
s_fontHeightScale = scale;
2023-05-08 14:33:37 +02:00
}
void R_SetFontScale(float scale)
{
2023-05-09 19:13:37 +02:00
s_fontGeneralScale = scale;
2023-05-08 14:33:37 +02:00
}
void R_SetFontZ(float zed)
{
2023-05-09 19:13:37 +02:00
s_fontZ = zed;
2023-05-08 14:33:37 +02:00
}
fontheader_t* R_LoadFont(const char* name)
{
2023-05-09 19:13:37 +02:00
int i;
char* theFile;
fontheader_t* header;
char* ref;
const char* token;
qboolean error;
error = qfalse;
for (i = 0; i < s_numLoadedFonts; i++)
{
header = &s_loadedFonts[i];
if (!Q_stricmp(name, header->name)) {
return header;
}
}
if (s_numLoadedFonts >= MAX_LOADED_FONTS)
{
ri.Printf(PRINT_WARNING, "LoadFont: Too many fonts loaded! Couldn't load %s\n", name);
return NULL;
}
va("fonts/%s.RitualFont", name);
if (ri.FS_ReadFile(va("fonts/%s.RitualFont", name), (void**)&theFile) == -1)
{
ri.Printf(PRINT_WARNING, "LoadFont: Couldn't load font %s\n", name);
return 0;
}
header = &s_loadedFonts[s_numLoadedFonts];
header->height = 0.0;
header->aspectRatio = 0.0;
Q_strncpyz(header->name, name, sizeof(header->name));
ref = theFile;
while (ref && !error)
{
token = COM_Parse(&ref);
if (!Q_stricmp(token, "RitFont"))
{
// ignore this token
continue;
}
if (!Q_stricmp(token, "indirections"))
{
token = COM_Parse(&ref);
if (Q_stricmp(token, "{"))
{
error = qtrue;
break;
}
for (i = 0; i < 256; i++)
{
token = COM_Parse(&ref);
if (!token[0]) {
error = qtrue;
break;
}
header->indirection[i] = atoi(token);
}
if (error) {
break;
}
token = COM_Parse(&ref);
if (Q_stricmp(token, "}"))
{
error = qtrue;
break;
}
}
else if (!Q_stricmp(token, "locations"))
{
token = COM_Parse(&ref);
if (Q_stricmp(token, "{"))
{
error = qtrue;
break;
}
for (i = 0; i < 256; i++)
{
token = COM_Parse(&ref);
if (Q_stricmp(token, "{")) {
error = qtrue;
break;
}
if (header->aspectRatio == 0.0)
{
ri.Printf(PRINT_WARNING, "WARNING: aspect decl must be before locations in font '%s'", name);
break;
}
header->locations[i].pos[0] = atof(COM_Parse(&ref)) / 256.0;
header->locations[i].pos[1] = atof(COM_Parse(&ref)) * header->aspectRatio / 256.0;
header->locations[i].size[0] = atof(COM_Parse(&ref)) / 256.0;
header->locations[i].size[1] = atof(COM_Parse(&ref)) * header->aspectRatio / 256.0;
token = COM_Parse(&ref);
if (Q_stricmp(token, "}"))
{
error = qtrue;
break;
}
}
if (error) {
break;
}
token = COM_Parse(&ref);
if (Q_stricmp(token, "}"))
{
error = qtrue;
break;
}
}
else if (!Q_stricmp(token, "height"))
{
// parse the header height
token = COM_Parse(&ref);
header->height = atof(token);
}
else if (!Q_stricmp(token, "aspect"))
{
// parse the aspect ratio
token = COM_Parse(&ref);
header->aspectRatio = atof(token);
}
else
{
// unknown token
break;
}
}
if (token[0])
{
ri.Printf(PRINT_WARNING, "WARNING: Unknown token '%s' parsing font '%s'\n", token, name);
error = qtrue;
}
R_LoadFontShader(header);
if (!header->height || !header->aspectRatio) {
// invalid height or aspect ratio
error = qtrue;
}
ri.FS_FreeFile(theFile);
if (error)
{
ri.Printf(3, "WARNING: Error parsing font %s.\n", name);
return NULL;
}
else
{
s_numLoadedFonts++;
return header;
}
2023-05-08 14:33:37 +02:00
return NULL;
}
2023-05-09 19:13:37 +02:00
void R_LoadFontShader(fontheader_t* font)
{
int i;
int save;
char filename[64];
shader_t* fontshader;
save = r_sequencenumber;
r_sequencenumber = -1;
Com_sprintf(filename, sizeof(filename), "gfx/fonts/%s", font->name);
font->shader = R_FindShader(filename, -1, qfalse, qfalse, qfalse, qfalse);
r_sequencenumber = save;
if (!font->shader) {
ri.Error(ERR_DROP, "Could not load font shader for %s\n", filename);
}
fontshader = (shader_t*)font->shader;
if (fontshader->numUnfoggedPasses > 0)
{
for (i = 0; i < fontshader->numUnfoggedPasses; i++)
{
if (fontshader->unfoggedStages[0] != NULL && fontshader->unfoggedStages[0]->active)
{
fontshader->unfoggedStages[0]->rgbGen = CGEN_GLOBAL_COLOR;
fontshader->unfoggedStages[0]->alphaGen = AGEN_GLOBAL_ALPHA;
}
}
font->trhandle = r_sequencenumber;
}
else
{
font->trhandle = r_sequencenumber;
}
}
2023-05-10 23:18:45 +02:00
void R_DrawString(fontheader_t* font, const char* text, float x, float y, int maxlen, qboolean bVirtualScreen) {
float charHeight;
float startx, starty;
int i;
float fWidthScale, fHeightScale;
i = 0;
startx = x;
starty = y;
fWidthScale = (double)glConfig.vidWidth / 640.0;
fHeightScale = (double)glConfig.vidHeight / 480.0;
if (!font) {
return;
}
R_SyncRenderThread();
if (font->trhandle != r_sequencenumber) {
font->shader = NULL;
}
if (!font->shader) {
R_LoadFontShader(font);
}
charHeight = s_fontHeightScale * font->height * s_fontGeneralScale;
RB_BeginSurface((shader_t*)font->shader);
for (i = 0; text[i]; i++) {
unsigned char c;
int indirected;
letterloc_t* loc;
c = text[i];
if (maxlen != -1 && i >= maxlen) {
break;
}
switch (c)
{
case '\t':
indirected = font->indirection[32];
if (indirected == -1) {
Com_DPrintf("R_DrawString: no space-character in font!\n");
} else {
x = s_fontGeneralScale * font->locations[indirected].size[0] * 256.0 * 3.0 + x;
}
break;
case '\n':
starty = charHeight + starty;
x = startx;
y = starty;
break;
case '\r':
x = startx;
break;
default:
indirected = font->indirection[c];
if (indirected == -1)
{
Com_DPrintf("R_DrawString: no 0x%02x-character in font!\n", c);
indirected = font->indirection['?'];
if (indirected == -1) {
Com_DPrintf("R_DrawString: no '?' character in font!\n");
break;
}
}
font->indirection[c] = indirected;
if (tess.numVertexes + 4 >= SHADER_MAX_VERTEXES || tess.numIndexes + 6 >= SHADER_MAX_INDEXES) {
RB_CheckOverflow(4, 6);
}
loc = &font->locations[indirected];
// texture coordinates
tess.texCoords[tess.numVertexes][0][0] = loc->pos[0];
tess.texCoords[tess.numVertexes][0][1] = loc->pos[1];
tess.texCoords[tess.numVertexes + 1][0][0] = loc->size[0] + loc->pos[0];
tess.texCoords[tess.numVertexes + 1][0][1] = loc->pos[1];
tess.texCoords[tess.numVertexes + 2][0][0] = loc->pos[0];
tess.texCoords[tess.numVertexes + 2][0][1] = loc->size[1] + loc->pos[1];
tess.texCoords[tess.numVertexes + 3][0][0] = loc->size[0] + loc->pos[0];
tess.texCoords[tess.numVertexes + 3][0][1] = loc->size[1] + loc->pos[1];
// vertices position
tess.xyz[tess.numVertexes][0] = x;
tess.xyz[tess.numVertexes][1] = y;
tess.xyz[tess.numVertexes][2] = s_fontZ;
tess.xyz[tess.numVertexes + 1][0] = s_fontGeneralScale * loc->size[0] * 256.0 + x;
tess.xyz[tess.numVertexes + 1][1] = y;
tess.xyz[tess.numVertexes + 1][2] = s_fontZ;
tess.xyz[tess.numVertexes + 2][0] = x;
tess.xyz[tess.numVertexes + 2][1] = charHeight + y;
tess.xyz[tess.numVertexes + 2][2] = s_fontZ;
tess.xyz[tess.numVertexes + 3][0] = s_fontGeneralScale * loc->size[0] * 256.0 + x;
tess.xyz[tess.numVertexes + 3][1] = charHeight + y;
tess.xyz[tess.numVertexes + 3][2] = s_fontZ;
// indices
tess.indexes[tess.numIndexes] = tess.numVertexes;
tess.indexes[tess.numIndexes + 1] = tess.numVertexes + 1;
tess.indexes[tess.numIndexes + 2] = tess.numVertexes + 2;
tess.indexes[tess.numIndexes + 3] = tess.numVertexes + 1;
tess.indexes[tess.numIndexes + 4] = tess.numVertexes + 3;
tess.indexes[tess.numIndexes + 5] = tess.numVertexes + 2;
if (bVirtualScreen)
{
// scale the string properly if virtual screen
tess.xyz[tess.numVertexes][0] *= fWidthScale;
tess.xyz[tess.numVertexes][1] *= fHeightScale;
tess.xyz[tess.numVertexes + 1][0] *= fWidthScale;
tess.xyz[tess.numVertexes + 1][1] *= fHeightScale;
tess.xyz[tess.numVertexes + 2][0] *= fWidthScale;
tess.xyz[tess.numVertexes + 2][1] *= fHeightScale;
tess.xyz[tess.numVertexes + 3][0] *= fWidthScale;
tess.xyz[tess.numVertexes + 3][1] *= fHeightScale;
}
tess.numVertexes += 4;
tess.numIndexes += 6;
break;
}
}
RB_EndSurface();
}
2023-05-10 23:18:45 +02:00
void R_DrawFloatingString(fontheader_t* font, const char* text, const vec3_t org, const vec4_t color, float scale, int maxlen) {
shader_t* fontshader;
qhandle_t fsh;
float charWidth, charHeight;
int i;
vec3_t pos;
polyVert_t verts[4];
if (!font) {
return;
}
R_SyncRenderThread();
if (font->trhandle != r_sequencenumber) {
font->shader = NULL;
}
if (!font->shader) {
R_LoadFontShader(font);
}
i = 0;
fontshader = (shader_t*)font->shader;
fsh = 0;
for (i = 0; i < tr.numShaders; i++)
{
if (fontshader == tr.shaders[i])
{
fsh = i;
break;
}
}
i = 0;
charHeight = font->height * s_fontHeightScale * s_fontGeneralScale * scale;
VectorCopy(org, pos);
for (i = 0; text[i]; i++) {
unsigned char c;
int indirected;
letterloc_t* loc;
c = text[i];
indirected = font->indirection[c];
if (indirected == -1)
{
Com_Printf("R_DrawFloatingString: no 0x%02x-character in font!\n", c);
continue;
}
loc = &font->locations[indirected];
// vertices color
verts[0].modulate[0] = (int)(color[0] * 255.0);
verts[0].modulate[1] = (int)(color[1] * 255.0);
verts[0].modulate[2] = (int)(color[2] * 255.0);
verts[0].modulate[3] = (int)(color[3] * 255.0);
verts[1].modulate[0] = verts[0].modulate[0];
verts[1].modulate[1] = verts[0].modulate[1];
verts[1].modulate[2] = verts[0].modulate[2];
verts[1].modulate[3] = verts[0].modulate[3];
verts[2].modulate[0] = verts[0].modulate[0];
verts[2].modulate[1] = verts[0].modulate[1];
verts[2].modulate[2] = verts[0].modulate[2];
verts[2].modulate[3] = verts[0].modulate[3];
verts[3].modulate[0] = verts[0].modulate[0];
verts[3].modulate[1] = verts[0].modulate[1];
verts[3].modulate[2] = verts[0].modulate[2];
verts[3].modulate[3] = verts[0].modulate[3];
// texture coordinates
verts[0].st[0] = loc->pos[0];
verts[0].st[1] = loc->pos[1];
verts[1].st[0] = loc->pos[0] + font->locations[indirected].size[0];
verts[1].st[1] = loc->pos[1];
verts[2].st[0] = verts[1].st[0];
verts[2].st[1] = loc->pos[1] + loc->size[1];
verts[3].st[0] = loc->pos[0];
verts[3].st[1] = verts[2].st[1];
VectorCopy(pos, verts[3].xyz);
charWidth = font->locations[indirected].size[0] * 256.0 * s_fontGeneralScale * scale;
verts[2].xyz[0] = pos[0] + tr.refdef.viewaxis[1][0] * -charWidth;
verts[2].xyz[1] = pos[1] + tr.refdef.viewaxis[1][1] * -charWidth;
verts[2].xyz[2] = pos[2] + tr.refdef.viewaxis[1][2] * -charWidth;
verts[1].xyz[0] = verts[2].xyz[0] + charHeight * tr.refdef.viewaxis[2][0];
verts[1].xyz[1] = verts[2].xyz[1] + charHeight * tr.refdef.viewaxis[2][1];
verts[1].xyz[2] = verts[2].xyz[2] + charHeight * tr.refdef.viewaxis[2][2];
verts[0].xyz[1] = verts[1].xyz[1] + tr.refdef.viewaxis[1][1] * charWidth;
verts[0].xyz[2] = verts[1].xyz[2] + tr.refdef.viewaxis[1][2] * charWidth;
verts[0].xyz[0] = verts[1].xyz[0] + tr.refdef.viewaxis[1][0] * charWidth;
if (RE_AddPolyToScene(fsh, 4, verts, 0)) {
++tr.refdef.numPolys;
}
pos[0] = verts[2].xyz[0];
pos[1] = verts[2].xyz[1];
pos[2] = verts[2].xyz[2];
}
2023-05-09 19:13:37 +02:00
}
float R_GetFontHeight(const fontheader_t* font)
{
2023-05-09 19:13:37 +02:00
if (!font) {
return 0.0;
}
return font->height * s_fontGeneralScale * s_fontHeightScale;
}
float R_GetFontStringWidth(const fontheader_t* font, const char* s)
{
2023-05-09 19:13:37 +02:00
float widths;
int i;
widths = 0.0;
if (!font) {
return 0.0;
}
for (i = 0; s[i]; i++)
{
int indirected;
char c = *s;
if (c == 9)
{
indirected = font->indirection[32];
if (indirected != -1) {
widths += font->locations[indirected].size[0] * 3.0;
} else {
Com_Printf("R_GetFontStringWidth: no space-character in font!\n");
}
}
else
{
indirected = font->indirection[c];
if (indirected != -1) {
widths += font->locations[indirected].size[0];
} else {
Com_Printf("R_GetFontStringWidth: no 0x%02x-character in font!\n", c);
}
}
}
return widths * s_fontGeneralScale * 256.0;
}