#include #include #include #include "utils/logger.h" #include "render/ui.h" #include "render/vertexbuffer.h" #include "render/indexbuffer.h" #include "render/renderdevice.h" #include "render/shadersystem.h" #include "render/texturesmanager.h" #include "render/texture2d.h" struct DrawInfo { uint16_t vxcount; uint16_t idxcount; }; struct UIGlobals { VertexBuffer* vb; IndexBuffer* ib; Shader* shader; Texture2D* activetexture = nullptr; Texture2D* defaultTexture = nullptr; UIVertex* vertices = nullptr; uint16_t* indices = nullptr; DrawInfo drawInfo[MAX_UI_VERTICES]; uint16_t count = 0; uint16_t position = 0; uint16_t Indexposition = 0; uint16_t currentIdx = 0; } g_ui; void uiInit() { ////////////////////////////////////////////////////////////////////////// // Buffer and context state // Buffer creation g_ui.vb = g_renderDevice->CreateVertexBuffer(nullptr, MAX_UI_VERTICES, true); g_ui.ib = g_renderDevice->CreateIndexBuffer(nullptr, MAX_UI_INDICES, true); // Create shader InputLayoutDesc_t inputLayout[] = { { VERTEXATTR_VEC2, SHADERSEMANTIC_POSITION }, { VERTEXATTR_VEC2, SHADERSEMANTIC_TEXCOORD }, { VERTEXATTR_VEC4, SHADERSEMANTIC_COLOR }, }; g_ui.shader = g_shaderSystem->CreateShader("ui", "content/shaders/ui_base.vs", "content/shaders/ui_tex.ps", inputLayout, sizeof(inputLayout) / sizeof(inputLayout[0])); g_ui.shader->m_stride = sizeof( UIVertex ); g_ui.defaultTexture = g_texturesManager->LoadTexture2D("$white$"); } void uiShutdown() { delete g_ui.ib; delete g_ui.vb; } void uiDumpBuffers() { LogMsg( "--- UI Vertex Buffer ---" ); g_ui.vertices = (UIVertex*)g_ui.vb->MapBuffer(BA_WRITE_ONLY); assert(g_ui.vertices); for ( int i = 0; i < g_ui.position; i++ ) { LogMsg( "%i: POSITION = %.2f %.2f", i, g_ui.vertices[ i ].position.x, g_ui.vertices[ i ].position.y ); LogMsg( "%i: TEXCOORD = %.2f %.2f", i, g_ui.vertices[ i ].uv.x, g_ui.vertices[ i ].uv.y ); LogMsg( "%i: COLOR = %.2f %.2f %.2f %.2f", i, g_ui.vertices[ i ].color.x, g_ui.vertices[ i ].color.y, g_ui.vertices[ i ].color.z, g_ui.vertices[ i ].color.w); } LogMsg( "------------------------" ); g_ui.vb->UnmapBuffer(); } void uiBeginRender() { g_ui.vertices = (UIVertex*)g_ui.vb->MapBuffer(BA_WRITE_ONLY); assert(g_ui.vertices); g_ui.indices = (uint16_t*)g_ui.ib->MapBuffer(BA_WRITE_ONLY); assert(g_ui.indices); } void uiDrawQuad(const Vec2& a, const Vec2& b, const Vec2& c, const Vec2& d, const Vec4& color) { DrawInfo& drawInfo = g_ui.drawInfo[g_ui.count]; drawInfo.vxcount = 4; drawInfo.idxcount = 6; g_ui.indices[g_ui.Indexposition + 0] = g_ui.currentIdx; g_ui.indices[g_ui.Indexposition + 1] = g_ui.currentIdx + 1; g_ui.indices[g_ui.Indexposition + 2] = g_ui.currentIdx + 2; g_ui.indices[g_ui.Indexposition + 3] = g_ui.currentIdx; g_ui.indices[g_ui.Indexposition + 4] = g_ui.currentIdx + 2; g_ui.indices[g_ui.Indexposition + 5] = g_ui.currentIdx + 3; g_ui.vertices[g_ui.position + 0].position = a; g_ui.vertices[g_ui.position + 0].color = color; g_ui.vertices[g_ui.position + 1].position = b; g_ui.vertices[g_ui.position + 1].color = color; g_ui.vertices[g_ui.position + 2].position = c; g_ui.vertices[g_ui.position + 2].color = color; g_ui.vertices[g_ui.position + 3].position = d; g_ui.vertices[g_ui.position + 3].color = color; // texcoord g_ui.vertices[g_ui.position + 0].uv = Vec2{ 0.0f, 1.0f }; g_ui.vertices[g_ui.position + 1].uv = Vec2{ 1.0f, 1.0f }; g_ui.vertices[g_ui.position + 2].uv = Vec2{ 1.0f, 0.0f }; g_ui.vertices[g_ui.position + 3].uv = Vec2{ 0.0f, 0.0f }; g_ui.currentIdx += 4; g_ui.Indexposition += 6; g_ui.position += 4; g_ui.count++; } static inline float Rsqrt(float x) { return 1.0f / sqrtf(x); } #define NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = Rsqrt(d2); VX *= inv_len; VY *= inv_len; } } (void)0 void uiDrawLines(const Vec2* drawPoints, const size_t pointsCount, bool closed, const Vec4& color) { float thickness = 2.1f; const int count = closed ? pointsCount : pointsCount - 1; const int vtxCount = (count) * 4; const int idxCount = (count) * 6; for (int i1 = 0; i1 < count; i1++) { const int i2 = (i1 + 1) == pointsCount ? 0 : i1 + 1; const Vec2& p1 = drawPoints[i1]; const Vec2& p2 = drawPoints[i2]; float dx = p2.x - p1.x; float dy = p2.y - p1.y; NORMALIZE2F_OVER_ZERO(dx, dy); dx *= (thickness * 0.5f); dy *= (thickness * 0.5f); g_ui.vertices[g_ui.position + 0].position.x = p1.x + dy; g_ui.vertices[g_ui.position + 0].position.y = p1.y - dx; g_ui.vertices[g_ui.position + 0].color = color; g_ui.vertices[g_ui.position + 1].position.x = p2.x + dy; g_ui.vertices[g_ui.position + 1].position.y = p2.y - dx; g_ui.vertices[g_ui.position + 1].color = color; g_ui.vertices[g_ui.position + 2].position.x = p2.x - dy; g_ui.vertices[g_ui.position + 2].position.y = p2.y + dx; g_ui.vertices[g_ui.position + 2].color = color; g_ui.vertices[g_ui.position + 3].position.x = p1.x - dy; g_ui.vertices[g_ui.position + 3].position.y = p1.y + dx; g_ui.vertices[g_ui.position + 3].color = color; g_ui.indices[g_ui.Indexposition + 0] = g_ui.currentIdx; g_ui.indices[g_ui.Indexposition + 1] = g_ui.currentIdx + 1; g_ui.indices[g_ui.Indexposition + 2] = g_ui.currentIdx + 2; g_ui.indices[g_ui.Indexposition + 3] = g_ui.currentIdx; g_ui.indices[g_ui.Indexposition + 4] = g_ui.currentIdx + 2; g_ui.indices[g_ui.Indexposition + 5] = g_ui.currentIdx + 3; g_ui.currentIdx += 4; g_ui.Indexposition += 6; g_ui.position += 4; DrawInfo& drawInfo = g_ui.drawInfo[g_ui.count]; drawInfo.vxcount = 4; drawInfo.idxcount = 6; g_ui.count++; } } void uiDrawRect(const Vec2& position, const Vec2& size, const Vec4& color) { Vec2 quad[4]; quad[0] = { position.x, position.y }; quad[1] = { position.x + size.x, position.y }; quad[2] = { position.x + size.x, position.y + size.y }; quad[3] = { position.x, position.y + size.y }; uiDrawQuad(quad[0], quad[1], quad[2], quad[3], color); } void uiSetTexture(Texture2D* texture) { g_ui.activetexture = texture ? texture : g_ui.defaultTexture; } void uiSetTextureByName(const char* filename) { uiSetTexture( g_texturesManager->LoadTexture2D( filename ) ); } void uiEndRender() { g_ui.indices = nullptr; g_ui.vertices = nullptr; g_ui.ib->UnmapBuffer(); g_ui.vb->UnmapBuffer(); int x = 0, y = 0, w = g_renderView.width, h = g_renderView.height; g_renderDevice->SetViewport(x, y, w, h); float L = x; float R = x + w; float T = y; float B = y + h; #if defined(GL_CLIP_ORIGIN) if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left #endif const float orthoProjection[4][4] = { { 2.0f / (R - L), 0.0f, 0.0f, 0.0f }, { 0.0f, 2.0f / (T - B), 0.0f, 0.0f }, { 0.0f, 0.0f, -1.0f, 0.0f }, { (R + L) / (L - R), (T + B) / (B - T), 0.0f, 1.0f }, }; if ( g_ui.activetexture == nullptr ) g_ui.activetexture = g_ui.defaultTexture; g_texturesManager->SetTexture( 0, g_ui.activetexture ); g_ui.activetexture->SetMin(TF_LINEAR); g_ui.activetexture->SetMag(TF_LINEAR); g_ui.activetexture->SetWrapS(TW_CLAMP_TO_EDGE); g_ui.activetexture->SetWrapT(TW_CLAMP_TO_EDGE); g_shaderSystem->SetShader( g_ui.shader ); g_shaderSystem->SetUniformMatrix( g_ui.shader, UNIFORM_PROJ_MATRIX, orthoProjection ); g_renderDevice->SetVerticesBuffer( g_ui.vb ); g_renderDevice->SetIndicesBuffer( g_ui.ib ); // Enable blending g_renderDevice->SetBlending( true ); g_renderDevice->SetBlendingFunction( BF_SRC_ALPHA, BF_ONE_MINUS_SRC_ALPHA ); // Disable depth g_renderDevice->SetDepthTest( false ); g_renderDevice->SetBackfaceCull( false ); g_renderDevice->DrawElements( PT_TRIANGLES, g_ui.Indexposition, true ); // Uncomment for UI VB debugging //uiDumpBuffers(); g_ui.currentIdx = 0; g_ui.Indexposition = 0; g_ui.position = 0; g_ui.count = 0; } typedef struct Character { int codePoint, x, y, width, height, originX, originY; } Character; typedef struct Font { const char* name; int size, bold, italic, width, height, characterCount; Character* characters; } Font;