mirror of
https://github.com/bearstonem/openmohaa-vr.git
synced 2026-10-25 22:04:41 +01:00
Two problems the first device run found, one hiding the other. GLimp_SetMode's fixedFunction branch offers exactly one context: desktop OpenGL 1.1. There is no such thing on Android, so window creation failed three times over and R_Init gave up with "Couldn't get a visual". renderergl2 never met this because it takes the other branch, which already knows how to ask for ES. gl4es is what makes the request answerable, and it wants the opposite of what the renderer believes it is getting: desktop GL 1.x presented upwards, OpenGL ES underneath. So the fixed function branch now asks for ES 3.0 and then ES 2.0, and the renderer is never told - it reads its version through gl4es, which reports desktop GL, and takes the desktop fixed function path on the strength of it. That got the renderer up, and revealed the second problem: the OpenXR session found no current EGL context. Which is where gl4es having its own opinion about glGetString stops being convenient. The engine's proof that a context is live is GL_VERSION coming back non-NULL, and gl4es answers VERSION, VENDOR and RENDERER out of constants without asking the driver - so a context that was created but never bound passes that test and is only noticed much later, somewhere else entirely. GLimp_SetMode now asks SDL directly whether the context it just made is current, restoring the check gl4es defeated. The session no longer assumes one is bound either. Being current is per thread, and a window that has not been shown yet has no surface to be current against, so VR_CreateSession asks for the binding through GLimp_MakeCurrent rather than requiring it to already be there, and reports the EGL handles when it cannot get one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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[0] != NULL && fontshader->stages[0]->active)
|
|
{
|
|
fontshader->stages[0]->rgbGen = CGEN_ENTITY;
|
|
fontshader->stages[0]->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;
|
|
}
|