added font rendering

This commit is contained in:
2026-05-25 01:35:19 +03:00
parent a9766d6dd6
commit f127ac3801
27 changed files with 657 additions and 43 deletions

View File

@@ -158,6 +158,8 @@ extern "C" void FunnyMain( int argc, char **argv )
fCurrent = Plat_GetTime();
};
Plat_Exit(0);
g_pClientGame->Shutdown();
g_pServerGame->Shutdown();
g_pRenderContext->Shutdown();

View File

@@ -1,23 +1,399 @@
#include "kottui/kottui.h"
#include "tier0/lib.h"
#include "tier0/mem.h"
#include "tier1/utlstring.h"
#include "tier2/ifilesystem.h"
#include "tier2/fileformats/json.h"
#include "trig.h"
struct GlyphData_t
{
uint32_t m_uCharacter;
float m_fX;
float m_fY;
float m_fWidth;
float m_fHeight;
};
class CKotRenderFont: public IKotRenderFont
{
public:
virtual IImage *GetAtlas() override;
virtual bool IsLetterPresent( uint32_t letter ) override;
virtual float GetLetterX( uint32_t letter ) override;
virtual float GetLetterY( uint32_t letter ) override;
virtual float GetWidth( uint32_t letter ) override;
virtual float GetHeight( uint32_t letter ) override;
virtual bool IsMono() override;
virtual uint32_t GetLetterWidth() override;
virtual uint32_t GetLetterHeight() override;
GlyphData_t *m_glyphs;
uint32_t m_uGlyphCount;
IImage *m_pAtlas;
};
class CKotUIBuffer: public IKotUIBuffer
{
public:
virtual void SetColor( char c ) = 0;
virtual void SetPosition( int iY, int iX ) = 0;
virtual void Printf(const char *szFormat, ...) = 0;
virtual void PutChar( char c ) = 0;
virtual void Clear( char c ) = 0;
virtual void SetPosition( int iY, int iX ) override;
virtual void Move( int iY, int iX ) override;
virtual void SetColor( char c ) override;
virtual void Clear( char c ) override;
virtual void PutChar( char c ) override;
virtual void Printf(const char *szFormat, ...) override;
virtual void Draw( IImage *pImage ) override;
virtual void SetTextSize( int iY, int iX ) override;
virtual void SetTextFont( IKotRenderFont *pFont ) override;
uint32_t *m_buffer;
ColorAlpha *m_primaryColor;
ColorAlpha *m_secondaryColor;
int m_iWidth = 0;
int m_iHeight = 0;
IBuffer *m_pDataBuffer;
IMaterial *m_pMaterial;
IRenderContext *m_pRenderContext;
IKotRenderFont *m_pFont;
uint32_t m_uCursor = 0;
};
class CKotUIManager: public IKotUIManager
{
public:
virtual void SetTextSize( int iY, int iX ) = 0;
virtual void SetTextFont( IRenderFont *pFont ) = 0;
virtual void Init() override;
virtual void Shutdown() override;
virtual void ConnectInterface( void *pIface, const char *szName ) override;
virtual void CreateBuffer( int iWidth, int iHeight, int iY, int iX ) = 0;
virtual void Draw( IRenderContext *pRenderContext );
virtual IKotRenderFont *LoadFont( const char *szPath ) override;
virtual IKotUIBuffer *CreateBuffer( int iWidth, int iHeight ) override;
virtual void DeleteBuffer( IKotUIBuffer *pBuffer ) override;
IRenderContext *m_pRenderContext;
ITextureArray *m_pFonts;
};
IKotUIManager *KotUIManager();
static IShader *s_pShader;
static CKotUIManager s_kotUIManager;
static IVertexBuffer *s_pGlyphBuffer;
IKotUIManager *KotUIManager()
{
return &s_kotUIManager;
}
EXPOSE_INTERFACE_FN(KotUIManager, IKotUIManager, KOT_UI_INTEFACE_VERSION);
IImage *CKotRenderFont::GetAtlas()
{
return m_pAtlas;
}
bool CKotRenderFont::IsLetterPresent( uint32_t letter )
{
for ( int i = 0; i < m_uGlyphCount; i++ )
{
GlyphData_t g = m_glyphs[i];
if (g.m_uCharacter == letter)
return true;
}
return false;
}
float CKotRenderFont::GetLetterX( uint32_t letter )
{
for ( int i = 0; i < m_uGlyphCount; i++ )
{
GlyphData_t g = m_glyphs[i];
if (g.m_uCharacter == letter)
return g.m_fX;
}
return 0;
}
float CKotRenderFont::GetLetterY( uint32_t letter )
{
for ( int i = 0; i < m_uGlyphCount; i++ )
{
GlyphData_t g = m_glyphs[i];
if (g.m_uCharacter == letter)
return g.m_fY;
}
return 0;
}
float CKotRenderFont::GetWidth( uint32_t letter )
{
for ( int i = 0; i < m_uGlyphCount; i++ )
{
GlyphData_t g = m_glyphs[i];
if (g.m_uCharacter == letter)
return g.m_fWidth;
}
return 0;
}
float CKotRenderFont::GetHeight( uint32_t letter )
{
for ( int i = 0; i < m_uGlyphCount; i++ )
{
GlyphData_t g = m_glyphs[i];
if (g.m_uCharacter == letter)
return g.m_fHeight;
}
return 0;
}
void CKotUIBuffer::SetPosition( int iY, int iX )
{
}
void CKotUIBuffer::Move( int iY, int iX )
{
m_uCursor = iY*m_iWidth+iX;
m_uCursor = m_uCursor % (m_iWidth * m_iHeight);
}
void CKotUIBuffer::SetColor( char c )
{
}
void CKotUIBuffer::Clear( char c )
{
}
void CKotUIBuffer::PutChar( char c )
{
m_buffer[m_uCursor] = c;
Move(0, m_uCursor+1);
}
void CKotUIBuffer::Printf(const char *szFormat, ...)
{
va_list vlArgs;
va_start(vlArgs, szFormat);
va_list vlArgs2;
va_copy(vlArgs2, vlArgs);
size_t nSize = V_vsnprintf(NULL, 0, szFormat, vlArgs2);
va_end(vlArgs2);
char *psz = (char*)V_malloc(nSize+1);
va_copy(vlArgs2, vlArgs);
V_vsnprintf(psz, nSize+1, szFormat, vlArgs2);
for ( size_t u = 0, i = m_uCursor; u < nSize; u++, i = (i + 1 % (m_iWidth * m_iHeight)) )
{
m_buffer[i] = psz[u];
}
va_end(vlArgs2);
V_free(psz);
va_end(vlArgs);
Move(0, m_uCursor+1);
}
struct TextDrawData_t
{
float m_vTexcoordOffsetX;
float m_vTexcoordOffsetY;
float m_vTexcoordSizeX;
float m_vTexcoordSizeY;
float m_fPosX;
float m_fPosY;
float m_fSizeX;
float m_fSizeY;
float m_fColor[4];
float m_fColor2[4];
};
struct ScreenData_t
{
float m_fScreenSizeX;
float m_fScreenSizeY;
};
void CKotUIBuffer::Draw( IImage *pImage )
{
uint32_t uGlyphCount = m_iWidth*m_iHeight;
uint32_t uRealGlyphCount = 0;
IRenderCommandList *pList = m_pRenderContext->CreateCommandList();
pList->StartRecording();
m_pDataBuffer->Lock();
TextDrawData_t *pData = (TextDrawData_t*)m_pDataBuffer->Map();
for ( uint32_t i = 0; i < uGlyphCount; i++ )
{
uint32_t l = m_buffer[i];
if ( !m_pFont->IsLetterPresent(l) )
continue;
pData[uRealGlyphCount].m_vTexcoordOffsetX = m_pFont->GetLetterX(l);
pData[uRealGlyphCount].m_vTexcoordOffsetY = m_pFont->GetLetterY(l);
pData[uRealGlyphCount].m_vTexcoordSizeX = m_pFont->GetWidth(l);
pData[uRealGlyphCount].m_vTexcoordSizeY = m_pFont->GetHeight(l);
pData[uRealGlyphCount].m_fSizeX = 16;
pData[uRealGlyphCount].m_fSizeY = 16;
pData[uRealGlyphCount].m_fPosX = i%m_iWidth*16;
pData[uRealGlyphCount].m_fPosY = i/m_iWidth*16;
uRealGlyphCount++;
}
m_pDataBuffer->Unmap();
m_pDataBuffer->Unlock();
IBuffer *pScreenBuffer = m_pRenderContext->CreateConstantBuffer(sizeof(ScreenData_t));
pScreenBuffer->Lock();
ScreenData_t *pScreenData = (ScreenData_t*)pScreenBuffer->Map();
pScreenData->m_fScreenSizeX = pImage->GetImageWidth();
pScreenData->m_fScreenSizeY = pImage->GetImageHeight();
pScreenBuffer->Unmap();
pScreenBuffer->Unlock();
m_pMaterial = m_pRenderContext->CreateMaterial(s_pShader);
m_pMaterial->PSSetShaderResource(0, m_pDataBuffer);
m_pMaterial->PSSetShaderResource(1, pScreenBuffer);
m_pMaterial->PSSetTexture(2, m_pFont->GetAtlas());
m_pMaterial->PSSetSampler(3, m_pRenderContext->GetDefaultSampler());
pList->SetRenderTarget(0, pImage);
pList->SetLoadStoreModes(0, LOAD_MODE_LOAD, STORE_MODE_STORE);
pList->SetRenderResolution(pImage->GetImageWidth(), pImage->GetImageHeight());
pList->SetMaterial(m_pMaterial);
pList->SetVertexBuffer(0, s_pGlyphBuffer);
pList->DrawPrimitives(6, 0, uRealGlyphCount, 0);
pList->EndRecording();
m_pRenderContext->SubmitCommandList(pList);
}
void CKotUIBuffer::SetTextSize( int iY, int iX )
{
}
void CKotUIBuffer::SetTextFont( IKotRenderFont *pFont )
{
m_pFont = pFont;
}
void CKotUIManager::Init()
{
s_pShader = m_pRenderContext->CreateShader("game/core/shaders/kottui.shader_c");
s_pShader->AddLayout(0, 8);
s_pShader->AddAttribute(0, 0, VERTEX_FORMAT_XY32_SFLOAT, 0);
s_pShader->AddOutputImage(0, IMAGE_FORMAT_RGBA8_UNORM);
s_pShader->Build();
s_pGlyphBuffer = m_pRenderContext->CreateVertexBuffer(sizeof(float)*12);
void *pBuffer = s_pGlyphBuffer->Map();
float vertices[12] = {
0, 0,
0, 1,
1, 0,
1, 0,
0, 1,
1, 1,
};
V_memcpy(pBuffer, vertices, sizeof(vertices));
s_pGlyphBuffer->Unmap();
m_pFonts = m_pRenderContext->CreateTextureArray();
}
void CKotUIManager::Shutdown()
{
}
void CKotUIManager::ConnectInterface( void *pFace, const char *psz )
{
if (!V_strcmp(psz, RENDER_CONTEXT_INTERFACE_VERSION))
m_pRenderContext = (IRenderContext*)pFace;
}
IKotRenderFont *CKotUIManager::LoadFont( const char *szPath )
{
CUtlString szAtlas = CUtlString("%s.png", szPath);
CUtlString szMeta = CUtlString("%s.fmd", szPath);
IFileHandle *pMetadataFile = filesystem->Open(szMeta, FILEMODE_READ);
if (!pMetadataFile)
return NULL;
const char *szMetadata = filesystem->ReadString(pMetadataFile);
filesystem->Close(pMetadataFile);
IJSONValue *pValue = JSONManager()->ReadString(szMetadata);
IJSONArray *pGlyphs = pValue->GetObject()->GetValue("glyphs")->GetArray();
uint32_t uAtlas = m_pFonts->LoadTexture(szAtlas);
CKotRenderFont *pFont = new CKotRenderFont;
pFont->m_pAtlas = pFont->m_pAtlas = m_pFonts->GetTexture(uAtlas);
uint32_t atlasWidth = pValue->GetObject()->GetValue("atlas")->GetObject()->GetValue("width")->GetNumberValue();
uint32_t atlasHeight = pValue->GetObject()->GetValue("atlas")->GetObject()->GetValue("height")->GetNumberValue();
pFont->m_uGlyphCount = 0;
pFont->m_glyphs = (GlyphData_t*)V_malloc(sizeof(GlyphData_t)*pGlyphs->GetCount());
for ( uint32_t i = 0; i < pGlyphs->GetCount(); i++ )
{
GlyphData_t glyph = {};
IJSONObject *pGlyph = pGlyphs->GetParameter(i)->GetObject();
if (pGlyph->GetValue("atlasBounds") == NULL)
continue;
IJSONObject *pAtlasBounds = pGlyph->GetValue("atlasBounds")->GetObject();
glyph.m_uCharacter = pGlyph->GetValue("unicode")->GetNumberValue();
glyph.m_fX = pAtlasBounds->GetValue("left")->GetNumberValue()/atlasWidth;
glyph.m_fY = 1-(pAtlasBounds->GetValue("bottom")->GetNumberValue()/atlasHeight);
glyph.m_fWidth = (
pAtlasBounds->GetValue("right")->GetNumberValue()
- pAtlasBounds->GetValue("left")->GetNumberValue()
) / atlasWidth;
glyph.m_fHeight = (
pAtlasBounds->GetValue("bottom")->GetNumberValue()
- pAtlasBounds->GetValue("top")->GetNumberValue()
) / atlasHeight;
pFont->m_glyphs[pFont->m_uGlyphCount++] = glyph;
}
return pFont;
}
IKotUIBuffer *CKotUIManager::CreateBuffer( int iWidth, int iHeight )
{
CKotUIBuffer *pBuffer = new CKotUIBuffer;
pBuffer->m_pRenderContext = m_pRenderContext;
pBuffer->m_buffer = (uint32_t*)V_malloc(sizeof(uint32_t)*iWidth*iHeight);
V_memset(pBuffer->m_buffer, 0, sizeof(uint32_t)*iWidth*iHeight);
pBuffer->m_iWidth = iWidth;
pBuffer->m_iHeight = iHeight;
pBuffer->m_pDataBuffer = m_pRenderContext->CreateStorageBuffer(sizeof(TextDrawData_t)*iWidth*iHeight);
return pBuffer;
}
void CKotUIManager::DeleteBuffer( IKotUIBuffer *pBuffer )
{
}