mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
509 lines
14 KiB
C++
509 lines
14 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
Doom 3 BFG Edition 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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 BFG Edition 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 Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "tr_local.h"
|
|
|
|
const char* DEFAULT_FONT = "Gothic821_Cn_BT";
|
|
|
|
static const float old_scale2 = 0.6f;
|
|
static const float old_scale1 = 0.3f;
|
|
|
|
/*
|
|
==============================
|
|
Old_SelectValueForScale
|
|
==============================
|
|
*/
|
|
ID_INLINE float Old_SelectValueForScale(float scale, float v0, float v1, float v2) {
|
|
return (scale >= old_scale2) ? v2 : (scale >= old_scale1) ? v1 : v0;
|
|
}
|
|
|
|
static void Font_ReadBigInt(idFile *fd, int &value) {
|
|
fd->Read(&value, sizeof(value));
|
|
value = BigLong(value);
|
|
}
|
|
|
|
static void Font_ReadBigShort(idFile *fd, short &value) {
|
|
fd->Read(&value, sizeof(value));
|
|
value = BigShort(value);
|
|
}
|
|
|
|
static bool Font_ReadTGAHeader(const char* filename, int &width, int &height) {
|
|
width = 0;
|
|
height = 0;
|
|
|
|
idFile* fd = fileSystem->OpenFileRead(filename);
|
|
if (fd == NULL) {
|
|
return false;
|
|
}
|
|
|
|
byte header[18];
|
|
const int bytesRead = fd->Read(header, sizeof(header));
|
|
fileSystem->CloseFile(fd);
|
|
|
|
if (bytesRead != sizeof(header)) {
|
|
return false;
|
|
}
|
|
|
|
width = header[12] | (header[13] << 8);
|
|
height = header[14] | (header[15] << 8);
|
|
return width > 0 && height > 0;
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
R_NormalizeFontName
|
|
==============================
|
|
*/
|
|
static idStr R_NormalizeFontName(const char* fontName) {
|
|
idStr cleanName = fontName ? fontName : "";
|
|
cleanName.StripQuotes();
|
|
cleanName.BackSlashesToSlashes();
|
|
cleanName.StripFileExtension();
|
|
|
|
if (cleanName.IsEmpty() || cleanName.Icmp("fonts") == 0 || cleanName.Icmp("default") == 0) {
|
|
return DEFAULT_FONT;
|
|
}
|
|
|
|
const char* newFontPrefix = "newfonts/";
|
|
if (idStr::Icmpn(cleanName.c_str(), newFontPrefix, strlen(newFontPrefix)) == 0) {
|
|
idStr rest = cleanName.c_str() + strlen(newFontPrefix);
|
|
int slash = rest.Find('/');
|
|
if (slash >= 0) {
|
|
rest = rest.Left(slash);
|
|
}
|
|
return rest.IsEmpty() ? DEFAULT_FONT : rest;
|
|
}
|
|
|
|
cleanName.StripPath();
|
|
return cleanName.IsEmpty() ? DEFAULT_FONT : cleanName;
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idRenderSystemLocal::RegisterFont
|
|
==============================
|
|
*/
|
|
idFont* idRenderSystemLocal::RegisterFont(const char* fontName) {
|
|
idStr cleanName = R_NormalizeFontName(fontName);
|
|
|
|
for (int i = 0; i < fonts.Num(); i++) {
|
|
if (idStr::Icmp(fonts[i]->GetName(), cleanName.c_str()) == 0) {
|
|
fonts[i]->Touch();
|
|
return fonts[i];
|
|
}
|
|
}
|
|
|
|
idFont* font = new idFont(cleanName.c_str());
|
|
fonts.Append(font);
|
|
return font;
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idRenderSystemLocal::FreeFonts
|
|
==============================
|
|
*/
|
|
void idRenderSystemLocal::FreeFonts() {
|
|
fonts.DeleteContents(true);
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idFont::RemapFont
|
|
==============================
|
|
*/
|
|
idFont* idFont::RemapFont(const char* baseName) {
|
|
idStr cleanName = R_NormalizeFontName(baseName);
|
|
|
|
if (cleanName.Icmp(DEFAULT_FONT) == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idFont::~idFont
|
|
==============================
|
|
*/
|
|
idFont::~idFont() {
|
|
if (fontInfo != NULL) {
|
|
Mem_Free(fontInfo->glyphData);
|
|
Mem_Free(fontInfo->charIndex);
|
|
delete fontInfo;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idFont::idFont
|
|
==============================
|
|
*/
|
|
idFont::idFont(const char* n) : name(R_NormalizeFontName(n)) {
|
|
fontInfo = NULL;
|
|
alias = RemapFont(name.c_str());
|
|
|
|
if (alias != NULL) {
|
|
for (idFont* f = alias; f != NULL; f = f->alias) {
|
|
if (f == this) {
|
|
common->FatalError("Font alias \"%s\" is a circular reference!", n);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!LoadFont()) {
|
|
if (name.Icmp(DEFAULT_FONT) == 0) {
|
|
common->FatalError("Could not load default font \"%s\"", DEFAULT_FONT);
|
|
}
|
|
else {
|
|
common->Warning("Could not load font %s", n);
|
|
alias = renderSystem->RegisterFont(DEFAULT_FONT);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct oldGlyphInfo_t {
|
|
int height;
|
|
int top;
|
|
int bottom;
|
|
int pitch;
|
|
int xSkip;
|
|
int imageWidth;
|
|
int imageHeight;
|
|
float s;
|
|
float t;
|
|
float s2;
|
|
float t2;
|
|
int junk;
|
|
char materialName[32];
|
|
};
|
|
static const int GLYPHS_PER_FONT = 256;
|
|
|
|
/*
|
|
==============================
|
|
LoadOldGlyphData
|
|
==============================
|
|
*/
|
|
bool LoadOldGlyphData(const char* filename, oldGlyphInfo_t glyphInfo[GLYPHS_PER_FONT]) {
|
|
idFile* fd = fileSystem->OpenFileRead(filename);
|
|
if (fd == NULL) {
|
|
return false;
|
|
}
|
|
fd->Read(glyphInfo, GLYPHS_PER_FONT * sizeof(oldGlyphInfo_t));
|
|
for (int i = 0; i < GLYPHS_PER_FONT; i++) {
|
|
glyphInfo[i].height = LittleLong(glyphInfo[i].height);
|
|
glyphInfo[i].top = LittleLong(glyphInfo[i].top);
|
|
glyphInfo[i].bottom = LittleLong(glyphInfo[i].bottom);
|
|
glyphInfo[i].pitch = LittleLong(glyphInfo[i].pitch);
|
|
glyphInfo[i].xSkip = LittleLong(glyphInfo[i].xSkip);
|
|
glyphInfo[i].imageWidth = LittleLong(glyphInfo[i].imageWidth);
|
|
glyphInfo[i].imageHeight = LittleLong(glyphInfo[i].imageHeight);
|
|
glyphInfo[i].s = LittleFloat(glyphInfo[i].s);
|
|
glyphInfo[i].t = LittleFloat(glyphInfo[i].t);
|
|
glyphInfo[i].s2 = LittleFloat(glyphInfo[i].s2);
|
|
glyphInfo[i].t2 = LittleFloat(glyphInfo[i].t2);
|
|
assert(glyphInfo[i].imageWidth == glyphInfo[i].pitch);
|
|
assert(glyphInfo[i].imageHeight == glyphInfo[i].height);
|
|
assert(glyphInfo[i].imageWidth == (glyphInfo[i].s2 - glyphInfo[i].s) * 256);
|
|
assert(glyphInfo[i].imageHeight == (glyphInfo[i].t2 - glyphInfo[i].t) * 256);
|
|
assert(glyphInfo[i].junk == 0);
|
|
}
|
|
fileSystem->CloseFile(fd);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idFont::LoadFont
|
|
==============================
|
|
*/
|
|
bool idFont::LoadFont() {
|
|
idStr fontName = va("newfonts/%s/48.dat", GetName());
|
|
idFile* fd = fileSystem->OpenFileRead(fontName);
|
|
if (fd == NULL) {
|
|
return false;
|
|
}
|
|
|
|
const int FONT_INFO_VERSION = 42;
|
|
const int FONT_INFO_MAGIC = (FONT_INFO_VERSION | ('i' << 24) | ('d' << 16) | ('f' << 8));
|
|
|
|
int version = 0;
|
|
Font_ReadBigInt(fd, version);
|
|
if (version != FONT_INFO_MAGIC) {
|
|
idLib::Warning("Wrong version in %s", GetName());
|
|
fileSystem->CloseFile(fd);
|
|
return false;
|
|
}
|
|
|
|
fontInfo = new fontInfo_t;
|
|
memset(fontInfo, 0, sizeof(*fontInfo));
|
|
|
|
short pointSize = 0;
|
|
|
|
Font_ReadBigShort(fd, pointSize);
|
|
assert(pointSize == 48);
|
|
|
|
Font_ReadBigShort(fd, fontInfo->ascender);
|
|
Font_ReadBigShort(fd, fontInfo->descender);
|
|
|
|
Font_ReadBigShort(fd, fontInfo->numGlyphs);
|
|
|
|
fontInfo->glyphData = (glyphInfo_t*)Mem_Alloc(sizeof(glyphInfo_t) * fontInfo->numGlyphs);
|
|
fontInfo->charIndex = (uint32_t*)Mem_Alloc(sizeof(uint32_t) * fontInfo->numGlyphs);
|
|
|
|
const int GLYPH_RECORD_SIZE = 10;
|
|
for (int i = 0; i < fontInfo->numGlyphs; i++) {
|
|
byte glyphBytes[GLYPH_RECORD_SIZE];
|
|
if (fd->Read(glyphBytes, sizeof(glyphBytes)) != sizeof(glyphBytes)) {
|
|
common->Warning("Short glyph data in %s", fontName.c_str());
|
|
fileSystem->CloseFile(fd);
|
|
return false;
|
|
}
|
|
|
|
fontInfo->glyphData[i].width = glyphBytes[0];
|
|
fontInfo->glyphData[i].height = glyphBytes[1];
|
|
fontInfo->glyphData[i].top = (char)glyphBytes[2];
|
|
fontInfo->glyphData[i].left = (char)glyphBytes[3];
|
|
fontInfo->glyphData[i].xSkip = glyphBytes[4];
|
|
fontInfo->glyphData[i].s = glyphBytes[6] | (glyphBytes[7] << 8);
|
|
fontInfo->glyphData[i].t = glyphBytes[8] | (glyphBytes[9] << 8);
|
|
}
|
|
|
|
fd->Read(fontInfo->charIndex, fontInfo->numGlyphs * sizeof(uint32_t));
|
|
for (int i = 0; i < fontInfo->numGlyphs; i++) {
|
|
fontInfo->charIndex[i] = LittleLong(fontInfo->charIndex[i]);
|
|
}
|
|
|
|
memset(fontInfo->ascii, -1, sizeof(fontInfo->ascii));
|
|
for (int i = 0; i < fontInfo->numGlyphs; i++) {
|
|
if (fontInfo->charIndex[i] < 128) {
|
|
fontInfo->ascii[fontInfo->charIndex[i]] = i;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
idStr fontTextureName = fontName;
|
|
fontTextureName.SetFileExtension("tga");
|
|
|
|
fontInfo->material = declManager->FindMaterial(fontTextureName);
|
|
fontInfo->material->SetSort(SS_GUI);
|
|
if (!Font_ReadTGAHeader(fontTextureName.c_str(), fontInfo->materialWidth, fontInfo->materialHeight)) {
|
|
fontInfo->materialWidth = fontInfo->material->GetImageWidth();
|
|
fontInfo->materialHeight = fontInfo->material->GetImageHeight();
|
|
}
|
|
|
|
int pointSizes[3] = { 12, 24, 48 };
|
|
float scales[3] = { 4.0f, 2.0f, 1.0f };
|
|
for (int i = 0; i < 3; i++) {
|
|
oldGlyphInfo_t oldGlyphInfo[GLYPHS_PER_FONT];
|
|
const char* oldFileName = va("newfonts/%s/old_%d.dat", GetName(), pointSizes[i]);
|
|
if (LoadOldGlyphData(oldFileName, oldGlyphInfo)) {
|
|
int mh = 0;
|
|
int mw = 0;
|
|
for (int g = 0; g < GLYPHS_PER_FONT; g++) {
|
|
if (mh < oldGlyphInfo[g].height) {
|
|
mh = oldGlyphInfo[g].height;
|
|
}
|
|
if (mw < oldGlyphInfo[g].xSkip) {
|
|
mw = oldGlyphInfo[g].xSkip;
|
|
}
|
|
}
|
|
fontInfo->oldInfo[i].maxWidth = scales[i] * mw;
|
|
fontInfo->oldInfo[i].maxHeight = scales[i] * mh;
|
|
}
|
|
else {
|
|
int mh = 0;
|
|
int mw = 0;
|
|
for (int g = 0; g < fontInfo->numGlyphs; g++) {
|
|
if (mh < fontInfo->glyphData[g].height) {
|
|
mh = fontInfo->glyphData[g].height;
|
|
}
|
|
if (mw < fontInfo->glyphData[g].xSkip) {
|
|
mw = fontInfo->glyphData[g].xSkip;
|
|
}
|
|
}
|
|
fontInfo->oldInfo[i].maxWidth = mw;
|
|
fontInfo->oldInfo[i].maxHeight = mh;
|
|
}
|
|
}
|
|
fileSystem->CloseFile(fd);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idFont::GetGlyphIndex
|
|
==============================
|
|
*/
|
|
int idFont::GetGlyphIndex(uint32_t idx) const {
|
|
if (idx < 128) {
|
|
return fontInfo->ascii[idx];
|
|
}
|
|
if (fontInfo->numGlyphs == 0) {
|
|
return -1;
|
|
}
|
|
if (fontInfo->charIndex == NULL) {
|
|
return idx;
|
|
}
|
|
int len = fontInfo->numGlyphs;
|
|
int mid = fontInfo->numGlyphs;
|
|
int offset = 0;
|
|
while (mid > 0) {
|
|
mid = len >> 1;
|
|
if (fontInfo->charIndex[offset + mid] <= idx) {
|
|
offset += mid;
|
|
}
|
|
len -= mid;
|
|
}
|
|
return (fontInfo->charIndex[offset] == idx) ? offset : -1;
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idFont::GetLineHeight
|
|
==============================
|
|
*/
|
|
float idFont::GetLineHeight(float scale) const {
|
|
if (alias != NULL) {
|
|
return alias->GetLineHeight(scale);
|
|
}
|
|
if (fontInfo != NULL) {
|
|
return scale * Old_SelectValueForScale(scale, fontInfo->oldInfo[0].maxHeight, fontInfo->oldInfo[1].maxHeight, fontInfo->oldInfo[2].maxHeight);
|
|
}
|
|
return 0.0f;
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idFont::GetAscender
|
|
==============================
|
|
*/
|
|
float idFont::GetAscender(float scale) const {
|
|
if (alias != NULL) {
|
|
return alias->GetAscender(scale);
|
|
}
|
|
if (fontInfo != NULL) {
|
|
return scale * fontInfo->ascender;
|
|
}
|
|
return 0.0f;
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idFont::GetMaxCharWidth
|
|
==============================
|
|
*/
|
|
float idFont::GetMaxCharWidth(float scale) const {
|
|
if (alias != NULL) {
|
|
return alias->GetMaxCharWidth(scale);
|
|
}
|
|
if (fontInfo != NULL) {
|
|
return scale * Old_SelectValueForScale(scale, fontInfo->oldInfo[0].maxWidth, fontInfo->oldInfo[1].maxWidth, fontInfo->oldInfo[2].maxWidth);
|
|
}
|
|
return 0.0f;
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idFont::GetGlyphWidth
|
|
==============================
|
|
*/
|
|
float idFont::GetGlyphWidth(float scale, uint32_t idx) const {
|
|
if (alias != NULL) {
|
|
return alias->GetGlyphWidth(scale, idx);
|
|
}
|
|
if (fontInfo != NULL) {
|
|
int i = GetGlyphIndex(idx);
|
|
const int asterisk = 42;
|
|
if (i == -1 && idx != asterisk) {
|
|
i = GetGlyphIndex(asterisk);
|
|
}
|
|
if (i >= 0) {
|
|
return scale * fontInfo->glyphData[i].xSkip;
|
|
}
|
|
}
|
|
return 0.0f;
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idFont::GetScaledGlyph
|
|
==============================
|
|
*/
|
|
void idFont::GetScaledGlyph(float scale, uint32_t idx, scaledGlyphInfo_t& glyphInfo) const {
|
|
if (alias != NULL) {
|
|
return alias->GetScaledGlyph(scale, idx, glyphInfo);
|
|
}
|
|
if (fontInfo != NULL) {
|
|
int i = GetGlyphIndex(idx);
|
|
const int asterisk = 42;
|
|
if (i == -1 && idx != asterisk) {
|
|
i = GetGlyphIndex(asterisk);
|
|
}
|
|
if (i >= 0) {
|
|
float invMaterialWidth = 1.0f / fontInfo->materialWidth;
|
|
float invMaterialHeight = 1.0f / fontInfo->materialHeight;
|
|
glyphInfo_t& gi = fontInfo->glyphData[i];
|
|
glyphInfo.xSkip = scale * gi.xSkip;
|
|
glyphInfo.top = scale * gi.top;
|
|
glyphInfo.left = scale * gi.left;
|
|
glyphInfo.width = scale * gi.width;
|
|
glyphInfo.height = scale * gi.height;
|
|
glyphInfo.s1 = (gi.s - 0.5f) * invMaterialWidth;
|
|
glyphInfo.t1 = (gi.t - 0.5f) * invMaterialHeight;
|
|
glyphInfo.s2 = (gi.s + gi.width + 0.5f) * invMaterialWidth;
|
|
glyphInfo.t2 = (gi.t + gi.height + 0.5f) * invMaterialHeight;
|
|
glyphInfo.material = fontInfo->material;
|
|
return;
|
|
}
|
|
}
|
|
memset(&glyphInfo, 0, sizeof(glyphInfo));
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
idFont::Touch
|
|
==============================
|
|
*/
|
|
void idFont::Touch() {
|
|
if (alias != NULL) {
|
|
alias->Touch();
|
|
}
|
|
if (fontInfo != NULL) {
|
|
const_cast<idMaterial*>(fontInfo->material)->EnsureNotPurged();
|
|
fontInfo->material->SetSort(SS_GUI);
|
|
}
|
|
}
|