Finish reimplementation of 2D text renderer
This commit is contained in:
parent
5020d82f68
commit
c30bb4f93c
@ -122,6 +122,33 @@ namespace Components
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TextRenderer::GlowColor(Game::GfxColor* result, const Game::GfxColor baseColor, const Game::GfxColor forcedGlowColor, int renderFlags)
|
||||
{
|
||||
if (renderFlags & Game::TEXT_RENDERFLAG_GLOW_FORCE_COLOR)
|
||||
{
|
||||
result->array[0] = forcedGlowColor.array[0];
|
||||
result->array[1] = forcedGlowColor.array[1];
|
||||
result->array[2] = forcedGlowColor.array[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
result->array[0] = static_cast<char>(std::floor(static_cast<float>(static_cast<uint8_t>(baseColor.array[0])) * 0.06f));
|
||||
result->array[1] = static_cast<char>(std::floor(static_cast<float>(static_cast<uint8_t>(baseColor.array[1])) * 0.06f));
|
||||
result->array[2] = static_cast<char>(std::floor(static_cast<float>(static_cast<uint8_t>(baseColor.array[2])) * 0.06f));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned TextRenderer::R_FontGetRandomLetter(const int seed)
|
||||
{
|
||||
static constexpr char RANDOM_CHARACTERS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
|
||||
return RANDOM_CHARACTERS[seed % (std::extent_v<decltype(RANDOM_CHARACTERS)> -1)];
|
||||
}
|
||||
|
||||
void TextRenderer::DrawTextFxExtraCharacter(Game::Material* material, const int charIndex, const float x, const float y, const float w, const float h, const float sinAngle, const float cosAngle, const unsigned color)
|
||||
{
|
||||
Game::RB_DrawStretchPicRotate(material, x, y, w, h, static_cast<float>(charIndex % 16) * 0.0625f, 0.0f, static_cast<float>(charIndex % 16) * 0.0625f + 0.0625f, 1.0f, sinAngle, cosAngle, color);
|
||||
}
|
||||
|
||||
float TextRenderer::DrawFontIcon(const std::string& fontIconName, float x, float y, float sinAngle, float cosAngle, const Game::Font_s* font, float xScale, const float yScale, unsigned color)
|
||||
{
|
||||
auto* material = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, fontIconName.data()).material;
|
||||
@ -215,6 +242,7 @@ namespace Components
|
||||
|
||||
int randSeed = 1;
|
||||
bool drawRandomCharAtEnd = false;
|
||||
const auto forceMonospace = renderFlags & Game::TEXT_RENDERFLAG_FORCEMONOSPACE;
|
||||
const auto monospaceWidth = GetMonospaceWidth(font, renderFlags);
|
||||
auto* material = font->material;
|
||||
Game::Material* glowMaterial = nullptr;
|
||||
@ -264,10 +292,14 @@ namespace Components
|
||||
|
||||
for(auto passIndex = 0u; passIndex < passCount; passIndex++)
|
||||
{
|
||||
float xRot, yRot;
|
||||
const char* curText = text;
|
||||
auto maxLengthRemaining = maxLength;
|
||||
auto currentColor = color.packed;
|
||||
auto currentColor = color;
|
||||
auto subtitleAllowGlow = false;
|
||||
auto extraFxChar = 0;
|
||||
auto drawExtraFxChar = false;
|
||||
auto passRandSeed = randSeed;
|
||||
auto count = 0;
|
||||
auto xa = startX;
|
||||
auto xy = startY;
|
||||
@ -282,20 +314,20 @@ namespace Components
|
||||
subtitleAllowGlow = false;
|
||||
if (colorIndex == TEXT_COLOR_DEFAULT)
|
||||
{
|
||||
currentColor = color.packed;
|
||||
currentColor = color;
|
||||
}
|
||||
else if (renderFlags & Game::TEXT_RENDERFLAG_SUBTITLETEXT && colorIndex == TEXT_COLOR_GREEN)
|
||||
{
|
||||
constexpr Game::GfxColor altColor{ MY_ALTCOLOR_TWO };
|
||||
subtitleAllowGlow = true;
|
||||
// Swap r and b for whatever reason
|
||||
currentColor = ColorRgba(altColor.array[2], altColor.array[1], altColor.array[0], Game::ModulateByteColors(altColor.array[3], color.array[3]));
|
||||
currentColor.packed = ColorRgba(altColor.array[2], altColor.array[1], altColor.array[0], Game::ModulateByteColors(altColor.array[3], color.array[3]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const Game::GfxColor colorTableColor{ (*currentColorTable)[colorIndex] };
|
||||
// Swap r and b for whatever reason
|
||||
currentColor = ColorRgba(colorTableColor.array[2], colorTableColor.array[1], colorTableColor.array[0], color.array[3]);
|
||||
currentColor.packed = ColorRgba(colorTableColor.array[2], colorTableColor.array[1], colorTableColor.array[0], color.array[3]);
|
||||
}
|
||||
|
||||
curText++;
|
||||
@ -303,11 +335,12 @@ namespace Components
|
||||
continue;
|
||||
}
|
||||
|
||||
auto finalColor = currentColor;
|
||||
|
||||
if(letter == '^' && (*curText == '\x01' || *curText == '\x02'))
|
||||
{
|
||||
float xRot, yRot;
|
||||
RotateXY(cosAngle, sinAngle, startX, startY, xa, xy, &xRot, &yRot);
|
||||
xa += DrawHudIcon(curText, xRot, yRot, sinAngle, cosAngle, font, xScale, yScale, currentColor);
|
||||
xa += DrawHudIcon(curText, xRot, yRot, sinAngle, cosAngle, font, xScale, yScale, finalColor.packed);
|
||||
|
||||
if (renderFlags & Game::TEXT_RENDERFLAG_PADDING)
|
||||
xa += xScale * padding;
|
||||
@ -321,9 +354,8 @@ namespace Components
|
||||
std::string fontIconName;
|
||||
if(IsFontIcon(curText, fontIconName))
|
||||
{
|
||||
float xRot, yRot;
|
||||
RotateXY(cosAngle, sinAngle, startX, startY, xa, xy, &xRot, &yRot);
|
||||
xa += DrawFontIcon(fontIconName, xRot, yRot, sinAngle, cosAngle, font, xScale, yScale, currentColor);
|
||||
xa += DrawFontIcon(fontIconName, xRot, yRot, sinAngle, cosAngle, font, xScale, yScale, ColorRgba(255, 255, 255, finalColor.array[3]));
|
||||
|
||||
if (renderFlags & Game::TEXT_RENDERFLAG_PADDING)
|
||||
xa += xScale * padding;
|
||||
@ -335,35 +367,108 @@ namespace Components
|
||||
|
||||
if(drawRandomCharAtEnd && maxLengthRemaining == 1)
|
||||
{
|
||||
|
||||
}
|
||||
letter = R_FontGetRandomLetter(Game::RandWithSeed(&passRandSeed));
|
||||
|
||||
if(passes[passIndex] == Game::FONTPASS_NORMAL)
|
||||
{
|
||||
if (renderFlags & Game::TEXT_RENDERFLAG_CURSOR && count == cursorPos)
|
||||
if(Game::RandWithSeed(&passRandSeed) % 2)
|
||||
{
|
||||
float xRot, yRot;
|
||||
RotateXY(cosAngle, sinAngle, startX, startY, xa, xy, &xRot, &yRot);
|
||||
Game::RB_DrawCursor(material, cursorLetter, xRot, yRot, sinAngle, cosAngle, font, xScale, yScale, color.packed);
|
||||
drawExtraFxChar = true;
|
||||
letter = 'O';
|
||||
}
|
||||
|
||||
float xRot, yRot;
|
||||
auto glyph = Game::R_GetCharacterGlyph(font, letter);
|
||||
auto xAdj = glyph->x0 * xScale;
|
||||
auto yAdj = glyph->y0 * yScale;
|
||||
RotateXY(cosAngle, sinAngle, startX, startY, xa + xAdj, xy + yAdj, &xRot, &yRot);
|
||||
Game::RB_DrawChar(material, xRot, yRot, static_cast<float>(glyph->pixelWidth) * xScale /** 1.75f*/, static_cast<float>(glyph->pixelHeight) * yScale /** 1.125f*/, sinAngle, cosAngle, glyph, currentColor);
|
||||
|
||||
xa += static_cast<float>(glyph->dx) * xScale;
|
||||
}
|
||||
|
||||
|
||||
auto skipDrawing = false;
|
||||
if(decaying)
|
||||
{
|
||||
char decayAlpha;
|
||||
Game::GetDecayingLetterInfo(letter, &passRandSeed, decayTimeElapsed, fxBirthTime, fxDecayDuration, currentColor.array[3], &skipDrawing, &decayAlpha, &letter, &drawExtraFxChar);
|
||||
finalColor.array[3] = decayAlpha;
|
||||
}
|
||||
|
||||
if(drawExtraFxChar)
|
||||
{
|
||||
auto tempSeed = passRandSeed;
|
||||
extraFxChar = Game::RandWithSeed(&tempSeed);
|
||||
}
|
||||
|
||||
auto glyph = Game::R_GetCharacterGlyph(font, letter);
|
||||
auto xAdj = static_cast<float>(glyph->x0) * xScale;
|
||||
auto yAdj = static_cast<float>(glyph->y0) * yScale;
|
||||
|
||||
if(!skipDrawing)
|
||||
{
|
||||
if (passes[passIndex] == Game::FONTPASS_NORMAL)
|
||||
{
|
||||
if (renderFlags & Game::TEXT_RENDERFLAG_DROPSHADOW)
|
||||
{
|
||||
auto ofs = 1.0f;
|
||||
if (renderFlags & Game::TEXT_RENDERFLAG_DROPSHADOW_EXTRA)
|
||||
ofs += 1.0f;
|
||||
|
||||
xRot = xa + xAdj + ofs;
|
||||
yRot = xy + yAdj + ofs;
|
||||
RotateXY(cosAngle, sinAngle, startX, startY, xRot, yRot, &xRot, &yRot);
|
||||
if (drawExtraFxChar)
|
||||
DrawTextFxExtraCharacter(fxMaterial, extraFxChar, xRot, yRot, static_cast<float>(glyph->pixelWidth) * xScale, static_cast<float>(glyph->pixelHeight) * yScale, sinAngle, cosAngle, dropShadowColor.packed);
|
||||
else
|
||||
Game::RB_DrawChar(material, xRot, yRot, static_cast<float>(glyph->pixelWidth) * xScale, static_cast<float>(glyph->pixelHeight) * yScale, sinAngle, cosAngle, glyph, dropShadowColor.packed);
|
||||
}
|
||||
|
||||
RotateXY(cosAngle, sinAngle, startX, startY, xa + xAdj, xy + yAdj, &xRot, &yRot);
|
||||
if (drawExtraFxChar)
|
||||
DrawTextFxExtraCharacter(fxMaterial, extraFxChar, xRot, yRot, static_cast<float>(glyph->pixelWidth) * xScale, static_cast<float>(glyph->pixelHeight) * yScale, sinAngle, cosAngle, finalColor.packed);
|
||||
else
|
||||
Game::RB_DrawChar(material, xRot, yRot, static_cast<float>(glyph->pixelWidth) * xScale, static_cast<float>(glyph->pixelHeight) * yScale, sinAngle, cosAngle, glyph, finalColor.packed);
|
||||
|
||||
if (renderFlags & Game::TEXT_RENDERFLAG_CURSOR && count == cursorPos)
|
||||
{
|
||||
RotateXY(cosAngle, sinAngle, startX, startY, xa, xy, &xRot, &yRot);
|
||||
Game::RB_DrawCursor(material, cursorLetter, xRot, yRot, sinAngle, cosAngle, font, xScale, yScale, color.packed);
|
||||
}
|
||||
}
|
||||
else if(passes[passIndex] == Game::FONTPASS_OUTLINE)
|
||||
{
|
||||
auto outlineSize = 1.0f;
|
||||
if (renderFlags & Game::TEXT_RENDERFLAG_OUTLINE_EXTRA)
|
||||
outlineSize = 1.3f;
|
||||
|
||||
for (const auto offset : MY_OFFSETS)
|
||||
{
|
||||
RotateXY(cosAngle, sinAngle, startX, startY, xa + xAdj + outlineSize * offset[0], xy + yAdj + outlineSize * offset[1], &xRot, &yRot);
|
||||
if (drawExtraFxChar)
|
||||
DrawTextFxExtraCharacter(fxMaterial, extraFxChar, xRot, yRot, static_cast<float>(glyph->pixelWidth) * xScale, static_cast<float>(glyph->pixelHeight) * yScale, sinAngle, cosAngle, dropShadowColor.packed);
|
||||
else
|
||||
Game::RB_DrawChar(material, xRot, yRot, static_cast<float>(glyph->pixelWidth) * xScale, static_cast<float>(glyph->pixelHeight) * yScale, sinAngle, cosAngle, glyph, dropShadowColor.packed);
|
||||
}
|
||||
}
|
||||
else if(passes[passIndex] == Game::FONTPASS_GLOW && ((renderFlags & Game::TEXT_RENDERFLAG_SUBTITLETEXT) == 0 || subtitleAllowGlow))
|
||||
{
|
||||
GlowColor(&finalColor, finalColor, glowForcedColor, renderFlags);
|
||||
|
||||
for (const auto offset : MY_OFFSETS)
|
||||
{
|
||||
RotateXY(cosAngle, sinAngle, startX, startY, xa + xAdj + 2.0f * offset[0] * xScale, xy + yAdj + 2.0f * offset[1] * yScale, &xRot, &yRot);
|
||||
if (drawExtraFxChar)
|
||||
DrawTextFxExtraCharacter(fxMaterialGlow, extraFxChar, xRot, yRot, static_cast<float>(glyph->pixelWidth) * xScale, static_cast<float>(glyph->pixelHeight) * yScale, sinAngle, cosAngle, finalColor.packed);
|
||||
else
|
||||
Game::RB_DrawChar(glowMaterial, xRot, yRot, static_cast<float>(glyph->pixelWidth) * xScale, static_cast<float>(glyph->pixelHeight) * yScale, sinAngle, cosAngle, glyph, finalColor.packed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(forceMonospace)
|
||||
xa += monospaceWidth * xScale;
|
||||
else
|
||||
xa += static_cast<float>(glyph->dx) * xScale;
|
||||
|
||||
if (renderFlags & Game::TEXT_RENDERFLAG_PADDING)
|
||||
xa += xScale * padding;
|
||||
|
||||
count++;
|
||||
maxLengthRemaining--;
|
||||
}
|
||||
|
||||
if(renderFlags & Game::TEXT_RENDERFLAG_CURSOR && count == cursorPos)
|
||||
{
|
||||
float xRot, yRot;
|
||||
RotateXY(cosAngle, sinAngle, startX, startY, xa, xy, &xRot, &yRot);
|
||||
Game::RB_DrawCursor(material, cursorLetter, xRot, yRot, sinAngle, cosAngle, font, xScale, yScale, color.packed);
|
||||
}
|
||||
|
@ -53,6 +53,13 @@ namespace Components
|
||||
static constexpr char COLOR_LAST_CHAR = CharForColorIndex(TEXT_COLOR_COUNT - 1);
|
||||
static constexpr unsigned MY_ALTCOLOR_TWO = 0x0DCE6FFE6;
|
||||
static constexpr unsigned COLOR_MAP_HASH = 0xA0AB1041;
|
||||
static constexpr float MY_OFFSETS[4][2]
|
||||
{
|
||||
{-1.0f, -1.0f},
|
||||
{-1.0f, 1.0f},
|
||||
{1.0f, -1.0f},
|
||||
{1.0f, 1.0f},
|
||||
};
|
||||
|
||||
static unsigned colorTableDefault[TEXT_COLOR_COUNT];
|
||||
static unsigned colorTableNew[TEXT_COLOR_COUNT];
|
||||
@ -72,6 +79,9 @@ namespace Components
|
||||
static float GetMonospaceWidth(Game::Font_s* font, int rendererFlags);
|
||||
static bool IsFontIcon(const char*& text, std::string& fontIconName);
|
||||
static Game::GfxImage* GetFontIconColorMap(Game::Material* fontIconMaterial);
|
||||
static void GlowColor(Game::GfxColor* result, Game::GfxColor baseColor, Game::GfxColor forcedGlowColor, int renderFlags);
|
||||
static unsigned R_FontGetRandomLetter(int seed);
|
||||
static void DrawTextFxExtraCharacter(Game::Material* material, int charIndex, float x, float y, float w, float h, float sinAngle, float cosAngle, unsigned color);
|
||||
static float DrawFontIcon(const std::string& fontIconName, float x, float y, float sinAngle, float cosAngle, const Game::Font_s* font, float xScale, float yScale, unsigned color);
|
||||
static float DrawHudIcon(const char*& text, float x, float y, float sinAngle, float cosAngle, const Game::Font_s* font, float xScale, float yScale, unsigned color);
|
||||
static void RotateXY(float cosAngle, float sinAngle, float pivotX, float pivotY, float x, float y, float* outX, float* outY);
|
||||
|
@ -336,6 +336,8 @@ namespace Game
|
||||
RB_DrawCursor_t RB_DrawCursor = RB_DrawCursor_t(0x534EA0);
|
||||
|
||||
Byte4PackRgba_t Byte4PackRgba = Byte4PackRgba_t(0x4FE910);
|
||||
RandWithSeed_t RandWithSeed = RandWithSeed_t(0x495580);
|
||||
GetDecayingLetterInfo_t GetDecayingLetterInfo = GetDecayingLetterInfo_t(0x5351C0);
|
||||
|
||||
XAssetHeader* DB_XAssetPool = reinterpret_cast<XAssetHeader*>(0x7998A8);
|
||||
unsigned int* g_poolSize = reinterpret_cast<unsigned int*>(0x7995E8);
|
||||
|
@ -783,6 +783,12 @@ namespace Game
|
||||
typedef void(__cdecl* Byte4PackRgba_t)(const float* from, char* to);
|
||||
extern Byte4PackRgba_t Byte4PackRgba;
|
||||
|
||||
typedef int(__cdecl* RandWithSeed_t)(int* seed);
|
||||
extern RandWithSeed_t RandWithSeed;
|
||||
|
||||
typedef void(__cdecl* GetDecayingLetterInfo_t)(unsigned int letter, int* randSeed, int decayTimeElapsed, int fxBirthTime, int fxDecayDuration, unsigned __int8 alpha, bool* resultSkipDrawing, char* resultAlpha, unsigned int* resultLetter, bool* resultDrawExtraFxChar);
|
||||
extern GetDecayingLetterInfo_t GetDecayingLetterInfo;
|
||||
|
||||
extern XAssetHeader* DB_XAssetPool;
|
||||
extern unsigned int* g_poolSize;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user