mirror of
https://github.com/bearstonem/openmohaa-vr.git
synced 2026-10-18 09:00:21 +02:00
Restore desktop HUD behavior and objective state, fix stereo text lifetimes, HUD placement and colors, sprite culling, and font shader passes. Handle swapchain timeouts and incomplete stereo frames safely, remove forced GPU waits, and correct rendering timing attribution. Track client-game source additions in CMake. Validated desktop and Android GL1/GL2 builds plus targeted regression and sanitizer checks. Headset appearance and performance remain unverified.
1024 lines
29 KiB
C++
1024 lines
29 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2024 the OpenMoHAA team
|
|
|
|
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
|
|
|
|
#include "tr_local.h"
|
|
|
|
#define MAX_LOADED_FONTS 255
|
|
|
|
static fontheader_sgl_t s_loadedFonts_sgl[MAX_LOADED_FONTS];
|
|
static int s_numLoadedFonts_sgl = 0;
|
|
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;
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
void R_SetFontHeightScale(float scale)
|
|
{
|
|
s_fontHeightScale = scale;
|
|
}
|
|
|
|
void R_SetFontScale(float scale)
|
|
{
|
|
s_fontGeneralScale = scale;
|
|
}
|
|
|
|
void R_SetFontZ(float zed)
|
|
{
|
|
s_fontZ = zed;
|
|
}
|
|
|
|
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)
|
|
{
|
|
int i;
|
|
char* theFile;
|
|
fontheader_sgl_t* header;
|
|
char* ref;
|
|
const char* token;
|
|
qboolean error;
|
|
|
|
error = qfalse;
|
|
|
|
for (i = 0; i < s_numLoadedFonts_sgl; i++)
|
|
{
|
|
header = &s_loadedFonts_sgl[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;
|
|
}
|
|
|
|
header = &s_loadedFonts_sgl[s_numLoadedFonts_sgl];
|
|
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_sgl++;
|
|
return header;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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[i] != NULL && fontshader->stages[i]->active)
|
|
{
|
|
fontshader->stages[i]->rgbGen = CGEN_ENTITY;
|
|
fontshader->stages[i]->alphaGen = AGEN_ENTITY;
|
|
}
|
|
}
|
|
|
|
font->trhandle = r_sequencenumber;
|
|
}
|
|
else
|
|
{
|
|
font->trhandle = r_sequencenumber;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
R_FontTraceBegin / R_FontTraceEnd
|
|
|
|
Temporary instrumentation, to settle whether the cost of the HUD is the strings
|
|
themselves. Both string paths flush the render command buffer and then drive the
|
|
back end directly, once per call - on a tiler with a framebuffer bound that can
|
|
end the render pass and force a resolve every time. Counting the calls alongside
|
|
the time spent in them says whether that is where the frame is going.
|
|
================
|
|
*/
|
|
static int s_fontMsec;
|
|
static int s_fontCalls;
|
|
static int s_fontLastReport;
|
|
|
|
static int R_FontTraceBegin( void ) {
|
|
return ri.Milliseconds();
|
|
}
|
|
|
|
static void R_FontTraceEnd( int start ) {
|
|
int now = ri.Milliseconds();
|
|
|
|
s_fontMsec += now - start;
|
|
s_fontCalls++;
|
|
|
|
if ( now - s_fontLastReport < 1000 ) {
|
|
return;
|
|
}
|
|
|
|
ri.Printf( PRINT_ALL, "VR font: %d calls, %dms a second\n", s_fontCalls, s_fontMsec );
|
|
|
|
s_fontLastReport = now;
|
|
s_fontCalls = 0;
|
|
s_fontMsec = 0;
|
|
}
|
|
|
|
void R_DrawString_sgl(fontheader_sgl_t* font, const char* text, float x, float y, int maxlen, const float *pvVirtualScreen) {
|
|
float charHeight;
|
|
float startx, starty;
|
|
int i;
|
|
float fWidthScale, fHeightScale;
|
|
|
|
i = 0;
|
|
startx = x;
|
|
starty = y;
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (!font) {
|
|
return;
|
|
}
|
|
|
|
const int fontTraceStart = R_FontTraceBegin();
|
|
|
|
R_IssuePendingRenderCommands();
|
|
|
|
if (font->trhandle != r_sequencenumber) {
|
|
font->shader = NULL;
|
|
}
|
|
|
|
if (!font->shader) {
|
|
R_LoadFontShader(font);
|
|
}
|
|
|
|
Vector4Copy(backEnd.color2D, backEnd.entity2D.e.shaderRGBA);
|
|
backEnd.currentEntity = &backEnd.entity2D;
|
|
|
|
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) {
|
|
ri.Printf(PRINT_DEVELOPER, "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)
|
|
{
|
|
ri.Printf(PRINT_DEVELOPER, "R_DrawString: no 0x%02x-character in font!\n", c);
|
|
indirected = font->indirection['?'];
|
|
if (indirected == -1) {
|
|
ri.Printf(PRINT_DEVELOPER, "R_DrawString: no '?' character in font!\n");
|
|
break;
|
|
}
|
|
// set the indirection for the next time
|
|
font->indirection[c] = indirected;
|
|
}
|
|
|
|
RB_CHECKOVERFLOW(4, 6);
|
|
|
|
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;
|
|
|
|
if (pvVirtualScreen)
|
|
{
|
|
// 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();
|
|
|
|
R_FontTraceEnd( fontTraceStart );
|
|
}
|
|
|
|
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) {
|
|
shader_t* fontshader;
|
|
qhandle_t fsh;
|
|
float charWidth, charHeight;
|
|
int i;
|
|
vec3_t pos;
|
|
polyVert_t verts[4];
|
|
|
|
if (!font) {
|
|
return;
|
|
}
|
|
|
|
R_IssuePendingRenderCommands();
|
|
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)
|
|
{
|
|
ri.Printf(PRINT_ALL, "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_AddPolyToScene2(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];
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
float R_GetFontHeight(const fontheader_t* font)
|
|
{
|
|
if (!font || !font->sgl[0]) {
|
|
return 0.0;
|
|
}
|
|
|
|
return font->sgl[0]->height * s_fontGeneralScale * s_fontHeightScale;
|
|
}
|
|
|
|
float R_GetFontStringWidth_sgl(const fontheader_sgl_t* font, const char* s)
|
|
{
|
|
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 == '\t')
|
|
{
|
|
indirected = font->indirection[32];
|
|
if (indirected != -1) {
|
|
widths += font->locations[indirected].size[0] * 3.0;
|
|
} else {
|
|
ri.Printf(PRINT_ALL, "R_GetFontStringWidth: no space-character in font!\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
indirected = font->indirection[c];
|
|
if (indirected != -1) {
|
|
widths += font->locations[indirected].size[0];
|
|
} else {
|
|
ri.Printf(PRINT_ALL, "R_GetFontStringWidth: no 0x%02x-character in font!\n", c);
|
|
}
|
|
}
|
|
}
|
|
|
|
return widths * s_fontGeneralScale * 256.0;
|
|
}
|
|
|
|
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;
|
|
}
|