2020-06-14 17:04:35 -04:00

196 lines
6.3 KiB
C++

#include "CFont.h"
#include "Core/GameProject/CResourceStore.h"
#include "Core/Render/CDrawUtil.h"
#include "Core/Render/CRenderer.h"
std::optional<CDynamicVertexBuffer> CFont::smGlyphVertices;
CIndexBuffer CFont::smGlyphIndices;
bool CFont::smBuffersInitialized = false;
CFont::CFont(CResourceEntry *pEntry) : CResource(pEntry)
{
}
CFont::~CFont() = default;
static constexpr float PtsToFloat(int32 Pt)
{
// This is a bit of an arbitrary number but it works
// 1 / (1280 / 1.333333f / 2)
return 0.00208333f * Pt;
}
std::unique_ptr<CDependencyTree> CFont::BuildDependencyTree() const
{
auto pOut = std::make_unique<CDependencyTree>();
pOut->AddDependency(mpFontTexture);
return pOut;
}
CVector2f CFont::RenderString(const TString& rkString, CRenderer* /*pRenderer*/, float /*AspectRatio*/,
CVector2f /*Position*/, CColor FillColor, CColor StrokeColor, uint32 FontSize)
{
// WIP
if (!smBuffersInitialized)
InitBuffers();
// Shader setup
CShader *pTextShader = CDrawUtil::GetTextShader();
pTextShader->SetCurrent();
const GLuint ModelMtxLoc = pTextShader->GetUniformLocation("ModelMtx");
const GLuint ColorLoc = pTextShader->GetUniformLocation("FontColor");
const GLuint LayerLoc = pTextShader->GetUniformLocation("RGBALayer");
mpFontTexture->Bind(0);
smGlyphVertices->Bind();
glDisable(GL_DEPTH_TEST);
// Initialize some more stuff before we start the character loop
CVector2f PrintHead(-1.f, 1.f);
const CTransform4f PtScale = CTransform4f::ScaleMatrix(PtsToFloat(1));
SGlyph *pPrevGlyph = nullptr;
float Scale;
if (FontSize == CFONT_DEFAULT_SIZE)
Scale = 1.f;
else
Scale = static_cast<float>(FontSize) / (mDefaultSize != 0 ? mDefaultSize : 18);
for (uint32 iChar = 0; iChar < rkString.Length(); iChar++)
{
// Get character, check for newline
const char Char = rkString[iChar];
if (Char == '\n')
{
pPrevGlyph = nullptr;
PrintHead.X = -1;
PrintHead.Y -= (PtsToFloat(mLineHeight) + PtsToFloat(mLineMargin) + PtsToFloat(mUnknown)) * Scale;
continue;
}
// Get glyph
auto iGlyph = mGlyphs.find(Char);
if (iGlyph == mGlyphs.end())
continue;
SGlyph *pGlyph = &iGlyph->second;
// Apply left padding and kerning
PrintHead.X += PtsToFloat(pGlyph->LeftPadding) * Scale;
if (pPrevGlyph)
{
if (pPrevGlyph->KerningIndex != UINT32_MAX)
{
for (uint32 iKern = pPrevGlyph->KerningIndex; iKern < mKerningTable.size(); iKern++)
{
if (mKerningTable[iKern].CharacterA != pPrevGlyph->Character) break;
if (mKerningTable[iKern].CharacterB == rkString[iChar])
{
PrintHead.X += PtsToFloat(mKerningTable[iKern].Adjust) * Scale;
break;
}
}
}
}
// Add a newline if this character goes over the right edge of the screen
if (PrintHead.X + ((PtsToFloat(pGlyph->PrintAdvance) + PtsToFloat(pGlyph->RightPadding)) * Scale) > 1)
{
PrintHead.X = -1;
PrintHead.Y -= (PtsToFloat(mLineHeight) + PtsToFloat(mLineMargin) + PtsToFloat(mUnknown)) * Scale;
if (Char == ' ') continue;
}
const float XTrans = PrintHead.X;
const float YTrans = PrintHead.Y + ((PtsToFloat(pGlyph->BaseOffset * 2) - PtsToFloat(mVerticalOffset * 2)) * Scale);
CTransform4f GlyphTransform = PtScale;
GlyphTransform.Scale(CVector3f(static_cast<float>(pGlyph->Width) / 2, static_cast<float>(pGlyph->Height), 1.f));
GlyphTransform.Scale(Scale);
GlyphTransform.Translate(CVector3f(XTrans, YTrans, 0.f));
// Get glyph layer
uint8 GlyphLayer = pGlyph->RGBAChannel;
if (mTextureFormat == 3)
GlyphLayer *= 2;
else if (mTextureFormat == 8)
GlyphLayer = 3;
// Load shader uniforms, buffer texture
glUniformMatrix4fv(ModelMtxLoc, 1, GL_FALSE, (GLfloat*) &GlyphTransform);
smGlyphVertices->BufferAttrib(EVertexAttribute::Tex0, pGlyph->TexCoords.data());
// Draw fill
glUniform1i(LayerLoc, GlyphLayer);
glUniform4fv(ColorLoc, 1, &FillColor.R);
smGlyphIndices.DrawElements();
// Draw stroke
if (mTextureFormat == 1 || mTextureFormat == 3 || mTextureFormat == 8)
{
uint8 StrokeLayer = 0;
if (mTextureFormat == 1)
StrokeLayer = 1;
else if (mTextureFormat == 3)
StrokeLayer = GlyphLayer + 1;
else if (mTextureFormat == 8)
StrokeLayer = GlyphLayer - 2;
glUniform1i(LayerLoc, StrokeLayer);
glUniform4fv(ColorLoc, 1, &StrokeColor.R);
smGlyphIndices.DrawElements();
}
// Update print head
PrintHead.X += PtsToFloat(pGlyph->PrintAdvance) * Scale;
PrintHead.X += PtsToFloat(pGlyph->RightPadding) * Scale;
pPrevGlyph = pGlyph;
}
glEnable(GL_DEPTH_TEST);
return PrintHead;
}
void CFont::InitBuffers()
{
smGlyphVertices.emplace();
smGlyphVertices->SetActiveAttribs(EVertexAttribute::Position | EVertexAttribute::Tex0);
smGlyphVertices->SetVertexCount(4);
static constexpr std::array Vertices{
CVector3f( 0.f, 0.f, 0.f),
CVector3f( 2.f, 0.f, 0.f),
CVector3f( 0.f, -2.f, 0.f),
CVector3f( 2.f, -2.f, 0.f)
};
smGlyphVertices->BufferAttrib(EVertexAttribute::Position, Vertices.data());
static constexpr std::array TexCoords{
CVector2f(0.f, 0.f),
CVector2f(1.f, 0.f),
CVector2f(0.f, 1.f),
CVector2f(1.f, 1.f)
};
smGlyphVertices->BufferAttrib(EVertexAttribute::Tex0, TexCoords.data());
smGlyphIndices.Reserve(4);
smGlyphIndices.AddIndex(0);
smGlyphIndices.AddIndex(2);
smGlyphIndices.AddIndex(1);
smGlyphIndices.AddIndex(3);
smGlyphIndices.SetPrimitiveType(GL_TRIANGLE_STRIP);
smBuffersInitialized = true;
}
void CFont::ShutdownBuffers()
{
if (smBuffersInitialized)
{
smGlyphVertices = std::nullopt;
smBuffersInitialized = false;
}
}