Files
unease/engine/render/ui.cpp
2025-03-09 04:50:41 +03:00

264 lines
8.0 KiB
C++

#include <cmath>
#include <cassert>
#include "utils/logger.h"
#include "utils/maths.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 vxcount;
uint16 idxcount;
};
struct UIGlobals {
VertexBuffer* vb;
IndexBuffer* ib;
Shader* shader;
Texture2D* activetexture;
Texture2D* defaultTexture;
UIVertex* vertices;
uint16* indices;
DrawInfo drawInfo[MAX_UI_VERTICES];
uint16 count;
uint16 position;
uint16 Indexposition;
uint16 currentIdx;
} g_ui;
void uiInit()
{
memset( &g_ui, 0, sizeof(g_ui) );
//////////////////////////////////////////////////////////////////////////
// Buffer and context state
// Buffer creation
g_ui.vb = g_renderDevice->CreateVertexBuffer(NULL, MAX_UI_VERTICES, true);
g_ui.ib = g_renderDevice->CreateIndexBuffer(NULL, 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", "shaders/ui_base.vs", "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;
memset(&g_ui, 0, sizeof(g_ui));
}
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*)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] = Vec2( position.x, position.y );
quad[1] = Vec2( position.x + size.x, position.y );
quad[2] = Vec2( position.x + size.x, position.y + size.y );
quad[3] = Vec2( 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 = NULL;
g_ui.vertices = NULL;
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 == NULL )
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;