#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 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 Init() override; virtual void Shutdown() override; virtual void ConnectInterface( void *pIface, const char *szName ) override; 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; }; 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 ) { }