2023-05-27 21:02:16 +02:00
|
|
|
/*
|
|
|
|
===========================================================================
|
2024-12-19 23:18:21 +01:00
|
|
|
Copyright (C) 2024 the OpenMoHAA team
|
2023-05-27 21:02:16 +02:00
|
|
|
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
|
|
|
|
OpenMoHAA source code 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.
|
|
|
|
|
|
|
|
OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
// tr_font.cpp -- font rendering
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
#include "tr_local.h"
|
2023-05-27 21:02:16 +02:00
|
|
|
|
|
|
|
#define MAX_LOADED_FONTS 255
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
static fontheader_sgl_t s_loadedFonts_sgl[MAX_LOADED_FONTS];
|
|
|
|
static int s_numLoadedFonts_sgl = 0;
|
2023-05-27 21:02:16 +02:00
|
|
|
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;
|
|
|
|
static float s_fontZ = 0.0;
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
void R_ShutdownFont() {
|
|
|
|
int i;
|
|
|
|
fontheader_t *header;
|
|
|
|
fontheader_sgl_t *header_sgl;
|
|
|
|
|
|
|
|
for (i = 0; i < s_numLoadedFonts; i++)
|
|
|
|
{
|
|
|
|
header = &s_loadedFonts[i];
|
|
|
|
if (header->charTable) {
|
|
|
|
ri.Free(header->charTable);
|
|
|
|
header->charTable = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(header, 0, sizeof(*header));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < s_numLoadedFonts_sgl; i++)
|
|
|
|
{
|
|
|
|
header_sgl = &s_loadedFonts_sgl[i];
|
|
|
|
memset(header_sgl, 0, sizeof(*header_sgl));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-27 21:02:16 +02:00
|
|
|
void R_SetFontHeightScale(float scale)
|
|
|
|
{
|
|
|
|
s_fontHeightScale = scale;
|
|
|
|
}
|
|
|
|
|
|
|
|
void R_SetFontScale(float scale)
|
|
|
|
{
|
|
|
|
s_fontGeneralScale = scale;
|
|
|
|
}
|
|
|
|
|
|
|
|
void R_SetFontZ(float zed)
|
|
|
|
{
|
|
|
|
s_fontZ = zed;
|
|
|
|
}
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
static int CodeSearch(const fontheader_t* font, unsigned short uch) {
|
|
|
|
int mid;
|
|
|
|
int l, r;
|
|
|
|
|
|
|
|
r = font->charTableLength;
|
|
|
|
l = 0;
|
|
|
|
while (l < r) {
|
|
|
|
mid = (l + r) / 2;
|
|
|
|
|
|
|
|
if (font->charTable[mid].cp > uch) {
|
|
|
|
r = (l + r) / 2;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uch == font->charTable[mid].cp) {
|
|
|
|
return (l + r) / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
l = mid + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uch != font->charTable[l].cp) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
|
|
|
static qboolean DBCSIsLeadByte(const fontheader_t* font, unsigned short uch) {
|
|
|
|
// Byte ranges found in Wikipedia articles with relevant search strings in each case
|
|
|
|
switch (font->codePage) {
|
|
|
|
case 932:
|
|
|
|
// Shift_jis
|
|
|
|
return ((uch >= 0x81) && (uch <= 0x9F)) ||
|
|
|
|
((uch >= 0xE0) && (uch <= 0xFC));
|
|
|
|
// Lead bytes F0 to FC may be a Microsoft addition.
|
|
|
|
case 936:
|
|
|
|
// GBK
|
|
|
|
return (uch >= 0x81) && (uch <= 0xFE);
|
|
|
|
case 949:
|
|
|
|
// Korean Wansung KS C-5601-1987
|
|
|
|
return (uch >= 0x81) && (uch <= 0xFE);
|
|
|
|
case 950:
|
|
|
|
// Big5
|
|
|
|
return (uch >= 0x81) && (uch <= 0xFE);
|
|
|
|
case 1361:
|
|
|
|
// Korean Johab KS C-5601-1992
|
|
|
|
return
|
|
|
|
((uch >= 0x84) && (uch <= 0xD3)) ||
|
|
|
|
((uch >= 0xD8) && (uch <= 0xDE)) ||
|
|
|
|
((uch >= 0xE0) && (uch <= 0xF9));
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
fontheader_sgl_t* R_LoadFont_sgl(const char* name)
|
2023-05-27 21:02:16 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char* theFile;
|
2024-12-19 23:18:21 +01:00
|
|
|
fontheader_sgl_t* header;
|
2023-05-27 21:02:16 +02:00
|
|
|
char* ref;
|
|
|
|
const char* token;
|
|
|
|
qboolean error;
|
|
|
|
|
|
|
|
error = qfalse;
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
for (i = 0; i < s_numLoadedFonts_sgl; i++)
|
2023-05-27 21:02:16 +02:00
|
|
|
{
|
2024-12-19 23:18:21 +01:00
|
|
|
header = &s_loadedFonts_sgl[i];
|
2023-05-27 21:02:16 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ri.FS_ReadFile(va("fonts/%s.RitualFont", name), (void**)&theFile) == -1)
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_WARNING, "LoadFont: Couldn't load font %s\n", name);
|
2024-12-19 23:18:21 +01:00
|
|
|
return NULL;
|
2023-05-27 21:02:16 +02:00
|
|
|
}
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
header = &s_loadedFonts_sgl[s_numLoadedFonts_sgl];
|
2023-05-27 21:02:16 +02:00
|
|
|
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;
|
2024-12-19 23:18:21 +01:00
|
|
|
|
2023-05-27 21:02:16 +02:00
|
|
|
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
|
|
|
|
{
|
2024-12-19 23:18:21 +01:00
|
|
|
s_numLoadedFonts_sgl++;
|
2023-05-27 21:02:16 +02:00
|
|
|
return header;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
fontheader_t* R_LoadFont(const char* name) {
|
|
|
|
int i;
|
|
|
|
char* theFile;
|
|
|
|
fontheader_t* header;
|
|
|
|
char* ref;
|
|
|
|
const char* token;
|
|
|
|
qboolean error;
|
|
|
|
char* pRitFontNames[32];
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(pRitFontNames, 0, sizeof(pRitFontNames));
|
|
|
|
ref = theFile;
|
|
|
|
header = &s_loadedFonts[s_numLoadedFonts];
|
|
|
|
|
|
|
|
token = COM_Parse(&ref);
|
|
|
|
if (Q_stricmp(token, "RitFontList"))
|
|
|
|
{
|
|
|
|
if (Q_stricmp(token, "RitFont"))
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_WARNING, "LoadFont: Not actual font %s\n", name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
header->numPages = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while (ref && !error)
|
|
|
|
{
|
|
|
|
token = COM_Parse(&ref);
|
|
|
|
if (!Q_stricmp(token, "CodePage"))
|
|
|
|
{
|
|
|
|
header->codePage = atoi(COM_Parse(&ref));
|
|
|
|
}
|
|
|
|
else if (!Q_stricmp(token, "Chars"))
|
|
|
|
{
|
|
|
|
header->charTableLength = atoi(COM_Parse(&ref));
|
|
|
|
header->charTable = (fontchartable_t*)ri.Malloc(header->charTableLength * sizeof(fontchartable_t));
|
|
|
|
if (!header->charTable)
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_WARNING, "LoadFont: Couldn't alloc mem %s\n", name);
|
|
|
|
error = qtrue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!Q_stricmp(token, "Pages"))
|
|
|
|
{
|
|
|
|
header->numPages = atoi(COM_Parse(&ref));
|
|
|
|
}
|
|
|
|
else if (!Q_stricmp(token, "RitFontName"))
|
|
|
|
{
|
|
|
|
token = COM_Parse(&ref);
|
|
|
|
if (Q_stricmp(token, "{"))
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_WARNING, "LoadFont: Bad Format %s\n", name);
|
|
|
|
error = qtrue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < header->numPages; i++)
|
|
|
|
{
|
|
|
|
token = COM_Parse(&ref);
|
|
|
|
if (!token[0])
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_WARNING, "LoadFont: Bad Token %s\n", name);
|
|
|
|
error = qtrue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pRitFontNames[i] = (char*)ri.Malloc(strlen(token) + 1);
|
|
|
|
if (!pRitFontNames[i])
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_WARNING, "LoadFont: Couldn't alloc mem %s\n", name);
|
|
|
|
error = qtrue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(pRitFontNames[i], token);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
token = COM_Parse(&ref);
|
|
|
|
if (Q_stricmp(token, "}"))
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_WARNING, "LoadFont: Bad Format %s\n", name);
|
|
|
|
error = qtrue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!Q_stricmp(token, "CharTable"))
|
|
|
|
{
|
|
|
|
token = COM_Parse(&ref);
|
|
|
|
if (Q_stricmp(token, "{"))
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_WARNING, "LoadFont: Bad Format %s\n", name);
|
|
|
|
error = qtrue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < header->charTableLength; i++) {
|
|
|
|
token = COM_Parse(&ref);
|
|
|
|
if (Q_stricmp(token, "{"))
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_WARNING, "LoadFont: Bad Token %s\n", name);
|
|
|
|
error = qtrue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
header->charTable[i].cp = atoi(COM_Parse(&ref));
|
|
|
|
header->charTable[i].index = atoi(COM_Parse(&ref));
|
|
|
|
header->charTable[i].loc = atoi(COM_Parse(&ref));
|
|
|
|
atoi(COM_Parse(&ref));
|
|
|
|
|
|
|
|
token = COM_Parse(&ref);
|
|
|
|
if (Q_stricmp(token, "}"))
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_WARNING, "LoadFont: Bad Format %s\n", name);
|
|
|
|
error = qtrue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
token = COM_Parse(&ref);
|
|
|
|
if (Q_stricmp(token, "}"))
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_WARNING, "LoadFont: Bad Format %s\n", name);
|
|
|
|
error = qtrue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_WARNING, "LoadFont: Bad Token %s\n", name);
|
|
|
|
error = qtrue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ri.FS_FreeFile(theFile);
|
|
|
|
|
|
|
|
if (!header->numPages)
|
|
|
|
{
|
|
|
|
header->charTableLength = 0;
|
|
|
|
header->charTable = NULL;
|
|
|
|
header->sgl[0] = R_LoadFont_sgl(name);
|
|
|
|
if (!header->sgl[0])
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_WARNING, "LoadFont: failed %s\n", name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (i = 0; i < header->numPages; i++)
|
|
|
|
{
|
|
|
|
header->sgl[i] = R_LoadFont_sgl(pRitFontNames[i]);
|
|
|
|
if (!header->sgl[i])
|
|
|
|
{
|
|
|
|
ri.Printf(PRINT_WARNING, "LoadFont: failed %s(%s)\n", pRitFontNames[i], name);
|
|
|
|
error = qtrue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free all allocated strings
|
|
|
|
for (i = 0; i < header->numPages; i++) {
|
|
|
|
ri.Free(pRitFontNames[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
if (header->charTable) {
|
|
|
|
ri.Free(header->charTable);
|
|
|
|
}
|
|
|
|
|
|
|
|
header->numPages = 0;
|
|
|
|
header->charTableLength = 0;
|
|
|
|
header->charTable = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(header->name, name);
|
|
|
|
s_numLoadedFonts++;
|
|
|
|
return header;
|
|
|
|
}
|
|
|
|
|
|
|
|
void R_LoadFontShader(fontheader_sgl_t* font)
|
2023-05-27 21:02:16 +02:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
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->stages[0] != NULL && fontshader->stages[0]->active)
|
|
|
|
{
|
2024-12-20 21:53:33 +01:00
|
|
|
fontshader->stages[0]->rgbGen = CGEN_ENTITY;
|
|
|
|
fontshader->stages[0]->alphaGen = AGEN_ENTITY;
|
2023-05-27 21:02:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
font->trhandle = r_sequencenumber;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
font->trhandle = r_sequencenumber;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
void R_DrawString_sgl(fontheader_sgl_t* font, const char* text, float x, float y, int maxlen, const float *pvVirtualScreen) {
|
2023-05-27 21:02:16 +02:00
|
|
|
float charHeight;
|
|
|
|
float startx, starty;
|
|
|
|
int i;
|
|
|
|
float fWidthScale, fHeightScale;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
startx = x;
|
|
|
|
starty = y;
|
2024-12-19 23:18:21 +01:00
|
|
|
if (pvVirtualScreen) {
|
|
|
|
if (pvVirtualScreen[0]) {
|
|
|
|
fWidthScale = pvVirtualScreen[0];
|
|
|
|
} else {
|
|
|
|
fWidthScale = (double)glConfig.vidWidth / 640.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pvVirtualScreen[1]) {
|
|
|
|
fHeightScale = pvVirtualScreen[1];
|
|
|
|
} else {
|
|
|
|
fHeightScale = (double)glConfig.vidHeight / 480.0;
|
|
|
|
}
|
|
|
|
}
|
2023-05-27 21:02:16 +02:00
|
|
|
|
|
|
|
if (!font) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
R_IssuePendingRenderCommands();
|
2023-05-27 21:02:16 +02:00
|
|
|
|
|
|
|
if (font->trhandle != r_sequencenumber) {
|
|
|
|
font->shader = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!font->shader) {
|
|
|
|
R_LoadFontShader(font);
|
|
|
|
}
|
|
|
|
|
2024-12-20 21:53:33 +01:00
|
|
|
Vector4Copy(backEnd.color2D, backEnd.entity2D.e.shaderRGBA);
|
|
|
|
backEnd.currentEntity = &backEnd.entity2D;
|
|
|
|
|
2023-05-27 21:02:16 +02:00
|
|
|
charHeight = s_fontHeightScale * font->height * s_fontGeneralScale;
|
|
|
|
RB_BeginSurface((shader_t*)font->shader, 0, 0);
|
|
|
|
|
|
|
|
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) {
|
2024-12-19 23:18:21 +01:00
|
|
|
ri.Printf(PRINT_DEVELOPER, "R_DrawString: no space-character in font!\n");
|
|
|
|
} else {
|
2023-05-27 21:02:16 +02:00
|
|
|
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)
|
|
|
|
{
|
2024-12-19 23:18:21 +01:00
|
|
|
ri.Printf(PRINT_DEVELOPER, "R_DrawString: no 0x%02x-character in font!\n", c);
|
2023-05-27 21:02:16 +02:00
|
|
|
indirected = font->indirection['?'];
|
|
|
|
if (indirected == -1) {
|
2024-12-19 23:18:21 +01:00
|
|
|
ri.Printf(PRINT_DEVELOPER, "R_DrawString: no '?' character in font!\n");
|
2023-05-27 21:02:16 +02:00
|
|
|
break;
|
2024-12-19 23:18:21 +01:00
|
|
|
}
|
2023-05-27 21:02:16 +02:00
|
|
|
// set the indirection for the next time
|
2024-12-19 23:18:21 +01:00
|
|
|
font->indirection[c] = indirected;
|
2023-05-27 21:02:16 +02:00
|
|
|
}
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
RB_CHECKOVERFLOW(4, 6);
|
2023-05-27 21:02:16 +02:00
|
|
|
|
|
|
|
loc = &font->locations[indirected];
|
|
|
|
|
|
|
|
// texture coordinates
|
|
|
|
tess.texCoords[tess.numVertexes][0] = loc->pos[0];
|
|
|
|
tess.texCoords[tess.numVertexes][1] = loc->pos[1];
|
|
|
|
tess.texCoords[tess.numVertexes + 1][0] = loc->size[0] + loc->pos[0];
|
|
|
|
tess.texCoords[tess.numVertexes + 1][1] = loc->pos[1];
|
|
|
|
tess.texCoords[tess.numVertexes + 2][0] = loc->pos[0];
|
|
|
|
tess.texCoords[tess.numVertexes + 2][1] = loc->size[1] + loc->pos[1];
|
|
|
|
tess.texCoords[tess.numVertexes + 3][0] = loc->size[0] + loc->pos[0];
|
|
|
|
tess.texCoords[tess.numVertexes + 3][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] = x + s_fontGeneralScale * loc->size[0] * 256.0;
|
|
|
|
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] = y + charHeight;
|
|
|
|
tess.xyz[tess.numVertexes + 2][2] = s_fontZ;
|
|
|
|
tess.xyz[tess.numVertexes + 3][0] = x + s_fontGeneralScale * loc->size[0] * 256.0;
|
|
|
|
tess.xyz[tess.numVertexes + 3][1] = y + charHeight;
|
|
|
|
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;
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
if (pvVirtualScreen)
|
2023-05-27 21:02:16 +02:00
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
x += s_fontGeneralScale * loc->size[0] * 256.0;
|
|
|
|
tess.numVertexes += 4;
|
|
|
|
tess.numIndexes += 6;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RB_EndSurface();
|
|
|
|
}
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
void R_DrawString(fontheader_t* font, const char* text, float x, float y, int maxlen, const float *pvVirtualScreen) {
|
|
|
|
int i;
|
|
|
|
int code;
|
|
|
|
unsigned short uch;
|
|
|
|
char buffer[512];
|
|
|
|
size_t buflen;
|
|
|
|
int cursgl;
|
|
|
|
float curX, curY;
|
|
|
|
float curHeight;
|
|
|
|
|
|
|
|
if (!font->numPages) {
|
|
|
|
if (font->sgl[0]) {
|
|
|
|
R_DrawString_sgl(font->sgl[0], text, x, y, maxlen, pvVirtualScreen);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (maxlen < 0) {
|
|
|
|
maxlen = strlen(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
curX = x;
|
|
|
|
curY = y;
|
|
|
|
curHeight = 0.f;
|
|
|
|
cursgl = -1;
|
|
|
|
buflen = 0;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
while(i < maxlen) {
|
|
|
|
fontchartable_t* ct;
|
|
|
|
|
|
|
|
uch = text[i];
|
|
|
|
i++;
|
|
|
|
|
|
|
|
if (DBCSIsLeadByte(font, uch)) {
|
|
|
|
uch = (uch << 8) | text[i];
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!uch) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uch == '\n' || uch == '\r') {
|
|
|
|
buffer[buflen] = 0;
|
|
|
|
R_DrawString_sgl(font->sgl[cursgl], buffer, curX, curY, maxlen, pvVirtualScreen);
|
|
|
|
|
|
|
|
curX = x;
|
|
|
|
curHeight = 0.f;
|
|
|
|
buflen = 0;
|
|
|
|
if (uch == '\n') {
|
|
|
|
curY += font->sgl[0]->height * s_fontGeneralScale * s_fontHeightScale;
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
code = CodeSearch(font, uch);
|
|
|
|
if (code < 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cursgl == -1) {
|
|
|
|
cursgl = font->charTable[code].index;
|
|
|
|
}
|
|
|
|
|
|
|
|
ct = &font->charTable[code];
|
|
|
|
if (cursgl != ct->index || buflen >= ARRAY_LEN(buffer) - 2) {
|
|
|
|
buffer[buflen] = 0;
|
|
|
|
R_DrawString_sgl(font->sgl[cursgl], buffer, curX, curY, maxlen, pvVirtualScreen);
|
|
|
|
|
|
|
|
curX += curHeight;
|
|
|
|
curHeight = 0.f;
|
|
|
|
buflen = 0;
|
|
|
|
ct = &font->charTable[code];
|
|
|
|
cursgl = ct->index;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer[buflen++] = ct->loc;
|
|
|
|
curHeight += font->sgl[cursgl]->locations[ct->loc].size[0] * 256.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buflen)
|
|
|
|
{
|
|
|
|
buffer[buflen] = 0;
|
|
|
|
R_DrawString_sgl(font->sgl[cursgl], buffer, curX, y, buflen, pvVirtualScreen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void R_DrawFloatingString_sgl(fontheader_sgl_t* font, const char* text, const vec3_t org, const vec4_t color, float scale, int maxlen) {
|
2023-05-27 21:02:16 +02:00
|
|
|
shader_t* fontshader;
|
|
|
|
qhandle_t fsh;
|
|
|
|
float charWidth, charHeight;
|
|
|
|
int i;
|
|
|
|
vec3_t pos;
|
|
|
|
polyVert_t verts[4];
|
|
|
|
|
|
|
|
if (!font) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
R_IssuePendingRenderCommands();
|
2023-05-27 21:02:16 +02:00
|
|
|
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;
|
2024-12-19 23:18:21 +01:00
|
|
|
|
2023-05-27 21:02:16 +02:00
|
|
|
c = text[i];
|
|
|
|
indirected = font->indirection[c];
|
|
|
|
if (indirected == -1)
|
|
|
|
{
|
2024-12-19 23:18:21 +01:00
|
|
|
ri.Printf(PRINT_ALL, "R_DrawFloatingString: no 0x%02x-character in font!\n", c);
|
2023-05-27 21:02:16 +02:00
|
|
|
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;
|
2024-12-19 23:18:21 +01:00
|
|
|
|
|
|
|
if (RE_AddPolyToScene2(fsh, 4, verts, 0)) {
|
|
|
|
++tr.refdef.numPolys;
|
|
|
|
}
|
2023-05-27 21:02:16 +02:00
|
|
|
|
|
|
|
pos[0] = verts[2].xyz[0];
|
|
|
|
pos[1] = verts[2].xyz[1];
|
|
|
|
pos[2] = verts[2].xyz[2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
void R_DrawFloatingString(fontheader_t* font, const char* text, const vec3_t org, const vec4_t color, float scale, int maxlen) {
|
|
|
|
return R_DrawFloatingString_sgl(font->sgl[0], text, org, color, scale, maxlen);
|
|
|
|
}
|
|
|
|
|
2023-05-27 21:02:16 +02:00
|
|
|
float R_GetFontHeight(const fontheader_t* font)
|
|
|
|
{
|
2024-12-19 23:18:21 +01:00
|
|
|
if (!font || !font->sgl[0]) {
|
2023-05-27 21:02:16 +02:00
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
return font->sgl[0]->height * s_fontGeneralScale * s_fontHeightScale;
|
2023-05-27 21:02:16 +02:00
|
|
|
}
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
float R_GetFontStringWidth_sgl(const fontheader_sgl_t* font, const char* s)
|
2023-05-27 21:02:16 +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;
|
|
|
|
|
2024-12-19 23:18:21 +01:00
|
|
|
if (c == '\t')
|
2023-05-27 21:02:16 +02:00
|
|
|
{
|
|
|
|
indirected = font->indirection[32];
|
|
|
|
if (indirected != -1) {
|
|
|
|
widths += font->locations[indirected].size[0] * 3.0;
|
2024-12-19 23:18:21 +01:00
|
|
|
} else {
|
|
|
|
ri.Printf(PRINT_ALL, "R_GetFontStringWidth: no space-character in font!\n");
|
2023-05-27 21:02:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
indirected = font->indirection[c];
|
|
|
|
if (indirected != -1) {
|
|
|
|
widths += font->locations[indirected].size[0];
|
2024-12-19 23:18:21 +01:00
|
|
|
} else {
|
|
|
|
ri.Printf(PRINT_ALL, "R_GetFontStringWidth: no 0x%02x-character in font!\n", c);
|
2023-05-27 21:02:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return widths * s_fontGeneralScale * 256.0;
|
|
|
|
}
|
2024-12-19 23:18:21 +01:00
|
|
|
|
|
|
|
float R_GetFontStringWidth(const fontheader_t* font, const char* s)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int code;
|
|
|
|
fontchartable_t* ct;
|
|
|
|
float width = 0.f;
|
|
|
|
|
|
|
|
if (!font->numPages) {
|
|
|
|
return R_GetFontStringWidth_sgl(font->sgl[0], s);
|
|
|
|
}
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
while(s[i]) {
|
|
|
|
unsigned char uch = s[i++];
|
|
|
|
|
|
|
|
if (DBCSIsLeadByte(font, uch)) {
|
|
|
|
uch = (uch << 8) | s[i++];
|
|
|
|
if (!uch) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uch == '\t') {
|
|
|
|
code = CodeSearch(font, ' ');
|
|
|
|
if (code >= 0) {
|
|
|
|
ct = &font->charTable[code];
|
|
|
|
width += font->sgl[ct->index]->locations[ct->loc].size[0] * 3.f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
code = CodeSearch(font, uch);
|
|
|
|
if (code >= 0) {
|
|
|
|
ct = &font->charTable[code];
|
|
|
|
width += font->sgl[ct->index]->locations[ct->loc].size[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return width;
|
|
|
|
}
|