strichmaennchen git · main
git clone https://git.christianimmanuel.de/games/strichmaennchen.gitwget https://git.christianimmanuel.de/games/strichmaennchen/archive/strichmaennchen.tar.gzui.c raw
#include "ui.h"
#include "glfuncs.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "shader.h" // createShaderProgram
// Vertex shader
static const char *ui_vert_src =
"#version 330 core\n"
"layout(location = 0) in vec2 in_pos;\n"
"layout(location = 1) in vec2 in_uv;\n"
"uniform vec2 u_screenSize;\n"
"out vec2 v_uv;\n"
"void main() {\n"
" vec2 ndc = (in_pos / u_screenSize) * 2.0 - 1.0;\n"
" gl_Position = vec4(ndc, 0.0, 1.0);\n"
" v_uv = in_uv;\n"
"}\n";
// Fragment shader
static const char *ui_frag_src =
"#version 330 core\n"
"in vec2 v_uv;\n"
"uniform sampler2D u_tex;\n"
"out vec4 out_color;\n"
"void main() {\n"
" float alpha = texture(u_tex, v_uv).r; // white text in texture → alpha\n"
" out_color = vec4(0.0, 0.0, 0.0, alpha);\n"
"}\n";
bool Ui_init(Ui *ui, Renderer *renderer, const char *font_path, int font_size) {
if (!ui || !renderer) return false;
memset(ui, 0, sizeof(*ui));
if (!TTF_Init()) {
SDL_Log("TTF_Init failed: %s", SDL_GetError());
return false;
}
ui->font = TTF_OpenFont(font_path, font_size);
if (!ui->font) {
SDL_Log("TTF_OpenFont failed: %s", SDL_GetError());
TTF_Quit();
return false;
}
ui->shader = createShaderProgram(ui_vert_src, ui_frag_src);
if (!ui->shader) {
SDL_Log("Ui shader compile failed");
TTF_CloseFont(ui->font);
TTF_Quit();
return false;
}
glGenVertexArrays(1, &ui->vao);
glGenBuffers(1, &ui->vbo);
int w, h;
SDL_GetWindowSizeInPixels(renderer->window, &w, &h);
ui->screen_w = w;
ui->screen_h = h;
ui->last_text[0] = '\0';
return true;
}
void Ui_resize(Ui *ui, SDL_Window *window) {
if (!ui || !window) return;
int w, h;
SDL_GetWindowSizeInPixels(window, &w, &h);
ui->screen_w = w;
ui->screen_h = h;
}
static void uploadTextureFromSurface(Ui *ui, SDL_Surface *surf) {
if (!surf) return;
SDL_Surface *conv = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA8888);
if (!conv) conv = surf;
else SDL_DestroySurface(surf);
if (ui->texture == 0) glGenTextures(1, &ui->texture);
glBindTexture(GL_TEXTURE_2D, ui->texture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, conv->w, conv->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, conv->pixels);
ui->tex_w = conv->w;
ui->tex_h = conv->h;
if (conv != surf) SDL_DestroySurface(conv);
glBindTexture(GL_TEXTURE_2D, 0);
}
void Ui_render(Ui *ui, Renderer *renderer, Time *time) {
if (!ui || !renderer) return;
// Save depth test state
GLboolean depthEnabled = glIsEnabled(GL_DEPTH_TEST);
glDisable(GL_DEPTH_TEST);
// Compose text
char buf[64];
snprintf(buf, sizeof(buf), "%02d:%02d:%02d", (int)time->hours, (int)time->minutes, (int)time->seconds);
if (strcmp(buf, ui->last_text) != 0) {
strncpy(ui->last_text, buf, sizeof(ui->last_text)-1);
ui->last_text[sizeof(ui->last_text)-1] = '\0';
// In Ui_render, create black text on transparent
SDL_Color color = {255, 255, 255, 255};
SDL_Surface *text_surf = TTF_RenderText_Blended(ui->font, buf, 0, color);
uploadTextureFromSurface(ui, text_surf);
SDL_DestroySurface(text_surf);
// Before drawing, enable blending so alpha works
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
if (ui->texture != 0) {
float margin = 15.0f;
float w = (float)ui->tex_w;
float h = (float)ui->tex_h;
// Top-right corner
float x = (float)ui->screen_w - w - margin;
float y = (float)ui->screen_h - h - margin;
float verts[] = {
x, y, 0.0f, 1.0f,
x+w, y, 1.0f, 1.0f,
x+w, y+h, 1.0f, 0.0f,
x, y, 0.0f, 1.0f,
x+w, y+h, 1.0f, 0.0f,
x, y+h, 0.0f, 0.0f
};
glUseProgram(ui->shader);
GLint screenLoc = glGetUniformLocation(ui->shader, "u_screenSize");
glUniform2f(screenLoc, (float)ui->screen_w, (float)ui->screen_h);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, ui->texture);
GLint texLoc = glGetUniformLocation(ui->shader, "u_tex");
glUniform1i(texLoc, 0);
glBindVertexArray(ui->vao);
glBindBuffer(GL_ARRAY_BUFFER, ui->vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (void*)(2*sizeof(float)));
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(0);
}
// Cleanup
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glUseProgram(0);
if (depthEnabled) glEnable(GL_DEPTH_TEST);
}
void Ui_shutdown(Ui *ui) {
if (!ui) return;
if (ui->texture) {
glDeleteTextures(1, &ui->texture);
ui->texture = 0;
}
if (ui->vbo) glDeleteBuffers(1, &ui->vbo);
if (ui->vao) glDeleteVertexArrays(1, &ui->vao);
if (ui->shader) glDeleteProgram(ui->shader);
if (ui->font) TTF_CloseFont(ui->font);
TTF_Quit();
}