Nimbin2git.christianimmanuel.de / Games / strichmaennchen / ui.c

strichmaennchen git · main

git clone https://git.christianimmanuel.de/games/strichmaennchen.gitwget https://git.christianimmanuel.de/games/strichmaennchen/archive/strichmaennchen.tar.gz
ui.c 5.4 KB · 188 lines 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();
}