Skip to content

Instantly share code, notes, and snippets.

@Lightnet
Created October 1, 2025 04:47
Show Gist options
  • Save Lightnet/e4b03d0bf7d982bc9d3b0663c02f316a to your computer and use it in GitHub Desktop.
Save Lightnet/e4b03d0bf7d982bc9d3b0663c02f316a to your computer and use it in GitHub Desktop.

Revisions

  1. Lightnet created this gist Oct 1, 2025.
    8 changes: 8 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,8 @@
    Flecs: 4.1
    Glad: 2.0.8
    opengl: 330
    sdl: 3.2
    just sample test.
    cimgui: 1.92

    This is just cube render set up test.
    243 changes: 243 additions & 0 deletions module_font.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,243 @@
    // module_font.c
    #include "module_font.h"
    #include <stdio.h>
    #include <stdlib.h>

    #define STB_TRUETYPE_IMPLEMENTATION
    #include "stb_truetype.h"

    // Define FontData locally
    struct FontData {
    GLuint texture; // Font texture ID
    stbtt_bakedchar* cdata; // Character data (ASCII 32..127)
    int bitmap_w, bitmap_h; // Bitmap dimensions
    };

    // Static FontData for alternative functions
    static struct FontData* global_font_data = NULL;

    // Initialize font: Load TTF and bake bitmap
    int init_font(const char* font_path, float font_size, float scale, FontData** font_data) {
    *font_data = (struct FontData*)malloc(sizeof(struct FontData));
    if (!*font_data) {
    printf("Error: Failed to allocate FontData\n");
    return 0;
    }

    unsigned char* ttf_buffer = (unsigned char*)malloc(1 << 20);
    if (!ttf_buffer) {
    printf("Error: Failed to allocate TTF buffer\n");
    free(*font_data);
    *font_data = NULL;
    return 0;
    }

    FILE* ff = fopen(font_path, "rb");
    if (!ff) {
    printf("Error: Failed to open font file '%s'\n", font_path);
    free(ttf_buffer);
    free(*font_data);
    *font_data = NULL;
    return 0;
    }

    fread(ttf_buffer, 1, 1 << 20, ff);
    fclose(ff);

    (*font_data)->bitmap_w = 512;
    (*font_data)->bitmap_h = 512;
    unsigned char* bitmap = (unsigned char*)malloc((*font_data)->bitmap_w * (*font_data)->bitmap_h);
    if (!bitmap) {
    printf("Error: Failed to allocate bitmap\n");
    free(ttf_buffer);
    free(*font_data);
    *font_data = NULL;
    return 0;
    }

    (*font_data)->cdata = (stbtt_bakedchar*)malloc(96 * sizeof(stbtt_bakedchar));
    if (!(*font_data)->cdata) {
    printf("Error: Failed to allocate cdata\n");
    free(bitmap);
    free(ttf_buffer);
    free(*font_data);
    *font_data = NULL;
    return 0;
    }

    stbtt_BakeFontBitmap(ttf_buffer, 0, font_size * scale, bitmap, (*font_data)->bitmap_w, (*font_data)->bitmap_h, 32, 96, (*font_data)->cdata);
    free(ttf_buffer);

    // Create OpenGL texture
    glGenTextures(1, &(*font_data)->texture);
    glBindTexture(GL_TEXTURE_2D, (*font_data)->texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, (*font_data)->bitmap_w, (*font_data)->bitmap_h, 0, GL_RED, GL_UNSIGNED_BYTE, bitmap);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    free(bitmap);

    return 1;
    }

    // Render text
    void render_text(FontData* font_data, GLuint program, GLuint vao, GLuint vbo, const char* text, float x, float y, int ww, int hh, float r, float g, float b, float a) {
    if (!font_data) return;

    float vertices[1024 * 4]; // Enough for simple text
    int vert_count = 0;

    for (const char* p = text; *p; p++) {
    if (*p >= 32 && *p < 128) {
    stbtt_aligned_quad q;
    stbtt_GetBakedQuad(font_data->cdata, font_data->bitmap_w, font_data->bitmap_h, *p - 32, &x, &y, &q, 1);

    float nx0 = 2.0f * q.x0 / ww - 1.0f;
    float ny0 = 1.0f - 2.0f * q.y0 / hh;
    float nx1 = 2.0f * q.x1 / ww - 1.0f;
    float ny1 = 1.0f - 2.0f * q.y1 / hh;

    // Triangle 1
    vertices[vert_count++] = nx0; vertices[vert_count++] = ny0; vertices[vert_count++] = q.s0; vertices[vert_count++] = q.t0;
    vertices[vert_count++] = nx1; vertices[vert_count++] = ny0; vertices[vert_count++] = q.s1; vertices[vert_count++] = q.t0;
    vertices[vert_count++] = nx1; vertices[vert_count++] = ny1; vertices[vert_count++] = q.s1; vertices[vert_count++] = q.t1;

    // Triangle 2
    vertices[vert_count++] = nx0; vertices[vert_count++] = ny0; vertices[vert_count++] = q.s0; vertices[vert_count++] = q.t0;
    vertices[vert_count++] = nx1; vertices[vert_count++] = ny1; vertices[vert_count++] = q.s1; vertices[vert_count++] = q.t1;
    vertices[vert_count++] = nx0; vertices[vert_count++] = ny1; vertices[vert_count++] = q.s0; vertices[vert_count++] = q.t1;
    }
    }

    glUseProgram(program);
    glUniform1i(glGetUniformLocation(program, "textTexture"), 0);
    glUniform4f(glGetUniformLocation(program, "textColor"), r, g, b, a);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, font_data->texture);

    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, vert_count * sizeof(float), vertices, GL_DYNAMIC_DRAW);
    glDrawArrays(GL_TRIANGLES, 0, vert_count / 4);
    glBindVertexArray(0);
    }

    // Clean up font resources
    void cleanup_font(FontData* font_data) {
    if (!font_data) return;
    glDeleteTextures(1, &font_data->texture);
    free(font_data->cdata);
    free(font_data);
    }

    // Alternative functions using a static FontData
    int init_font_alt(const char* font_path, float font_size, float scale) {
    if (global_font_data) {
    cleanup_font(global_font_data);
    }
    return init_font(font_path, font_size, scale, &global_font_data);
    }

    void render_text_alt(GLuint program, GLuint vao, GLuint vbo, const char* text, float x, float y, int ww, int hh, float r, float g, float b, float a) {
    if (global_font_data) {
    render_text(global_font_data, program, vao, vbo, text, x, y, ww, hh, r, g, b, a);
    }
    }

    void cleanup_font_alt(void) {
    if (global_font_data) {
    cleanup_font(global_font_data);
    global_font_data = NULL;
    }
    }

    // Initialize shaders and VAO/VBO
    int init_font_shaders_and_buffers(GLuint* program, GLuint* vao, GLuint* vbo) {
    const char* vs_src =
    "#version 330 core\n"
    "layout(location = 0) in vec2 position;\n"
    "layout(location = 1) in vec2 texCoord;\n"
    "out vec2 TexCoord;\n"
    "void main() {\n"
    " gl_Position = vec4(position, 0.0, 1.0);\n"
    " TexCoord = texCoord;\n"
    "}\n";

    const char* fs_src =
    "#version 330 core\n"
    "in vec2 TexCoord;\n"
    "out vec4 FragColor;\n"
    "uniform sampler2D textTexture;\n"
    "uniform vec4 textColor;\n"
    "void main() {\n"
    " float alpha = texture(textTexture, TexCoord).r;\n"
    " FragColor = vec4(textColor.rgb, alpha * textColor.a);\n"
    "}\n";

    GLuint vs = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vs, 1, &vs_src, NULL);
    glCompileShader(vs);
    GLint success;
    glGetShaderiv(vs, GL_COMPILE_STATUS, &success);
    if (!success) {
    char info_log[512];
    glGetShaderInfoLog(vs, 512, NULL, info_log);
    printf("Vertex shader compilation failed: %s\n", info_log);
    return 0;
    }

    GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fs, 1, &fs_src, NULL);
    glCompileShader(fs);
    glGetShaderiv(fs, GL_COMPILE_STATUS, &success);
    if (!success) {
    char info_log[512];
    glGetShaderInfoLog(fs, 512, NULL, info_log);
    printf("Fragment shader compilation failed: %s\n", info_log);
    glDeleteShader(vs);
    return 0;
    }

    *program = glCreateProgram();
    glAttachShader(*program, vs);
    glAttachShader(*program, fs);
    glLinkProgram(*program);
    glGetProgramiv(*program, GL_LINK_STATUS, &success);
    if (!success) {
    char info_log[512];
    glGetProgramInfoLog(*program, 512, NULL, info_log);
    printf("Program linking failed: %s\n", info_log);
    glDeleteShader(vs);
    glDeleteShader(fs);
    return 0;
    }

    glDeleteShader(vs);
    glDeleteShader(fs);

    glGenVertexArrays(1, vao);
    glGenBuffers(1, vbo);
    glBindVertexArray(*vao);
    glBindBuffer(GL_ARRAY_BUFFER, *vbo);
    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)));
    glBindVertexArray(0);

    return 1;
    }















    37 changes: 37 additions & 0 deletions module_font.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,37 @@
    // module_font.h
    #pragma once

    // #ifndef MODULE_FONT_H
    // #define MODULE_FONT_H

    #include <glad/gl.h>

    // Opaque pointer to FontData
    typedef struct FontData FontData;

    int init_font_shaders_and_buffers(GLuint* program, GLuint* vao, GLuint* vbo);

    // Functions using FontData
    int init_font(const char* font_path, float font_size, float scale, FontData** font_data);
    void render_text(FontData* font_data, GLuint program, GLuint vao, GLuint vbo, const char* text, float x, float y, int ww, int hh, float r, float g, float b, float a);
    void cleanup_font(FontData* font_data);

    // Alternative functions (no FontData, for internal management)
    int init_font_alt(const char* font_path, float font_size, float scale);
    void render_text_alt(GLuint program, GLuint vao, GLuint vbo, const char* text, float x, float y, int ww, int hh, float r, float g, float b, float a);
    void cleanup_font_alt(void);

    // #endif // MODULE_FONT_H



    /*
    // refs
    typedef struct {
    GLuint texture;
    stbtt_bakedchar* cdata;
    int bitmap_w, bitmap_h;
    } FontData;
    render_text(&font_data, text_program, text_vao, text_vbo, text, 25.0f, 150.0f, ww, hh, 1.0f, 1.0f, 1.0f, 1.0f);
    */
    596 changes: 596 additions & 0 deletions sdl3_glad_cimgui_transformer3d04.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,596 @@
    //

    #include <SDL3/SDL.h>
    #include <glad/gl.h>
    #include <stdio.h>
    #include <stdlib.h>

    #include <cimgui.h>
    #include <cimgui_impl.h>

    #include "module_font.h"
    #include <cglm/cglm.h> // Include CGLM
    #include "flecs.h"

    #define igGetIO igGetIO_Nil

    typedef struct {
    float x;
    float y;
    } Position;

    typedef struct {
    float x;
    float y;
    } Velocity;


    typedef struct {
    vec3 position; // Vector3 for position (x, y, z)
    vec3 rotation; // Euler angles in degrees (x, y, z)
    vec3 scale; // Scale (x, y, z)
    mat4 local; // Local transformation matrix
    mat4 world; // World transformation matrix
    bool isDirty; // Flag to indicate if transform needs recalculation
    } Transform3D;
    ECS_COMPONENT_DECLARE(Transform3D);

    typedef struct {
    GLuint vao, vbo, ebo; // OpenGL buffer objects
    GLuint shaderProgram; // Shader program for the cube
    int indexCount; // Number of indices for rendering
    } CubeContext;


    // Cube vertices: position (x, y, z)
    static const float cubeVertices[] = {
    // Front face
    -0.5f, -0.5f, 0.5f, // 0
    0.5f, -0.5f, 0.5f, // 1
    0.5f, 0.5f, 0.5f, // 2
    -0.5f, 0.5f, 0.5f, // 3
    // Back face
    -0.5f, -0.5f, -0.5f, // 4
    0.5f, -0.5f, -0.5f, // 5
    0.5f, 0.5f, -0.5f, // 6
    -0.5f, 0.5f, -0.5f // 7
    };


    // Indices for the cube (6 faces, 2 triangles per face, 3 vertices per triangle)
    static const unsigned int cubeIndices[] = {
    // Front
    0, 1, 2, 2, 3, 0,
    // Right
    1, 5, 6, 6, 2, 1,
    // Back
    5, 4, 7, 7, 6, 5,
    // Left
    4, 0, 3, 3, 7, 4,
    // Top
    3, 2, 6, 6, 7, 3,
    // Bottom
    4, 5, 1, 1, 0, 4
    };

    bool init_cube_mesh(CubeContext* cube) {
    // Vertex Shader
    const char* vertexShaderSource = "#version 330 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "uniform mat4 model;\n"
    "uniform mat4 view;\n"
    "uniform mat4 projection;\n"
    "void main() {\n"
    " gl_Position = projection * view * model * vec4(aPos, 1.0);\n"
    "}\n";

    // Fragment Shader (simple color)
    const char* fragmentShaderSource = "#version 330 core\n"
    "out vec4 FragColor;\n"
    "void main() {\n"
    " FragColor = vec4(1.0, 0.5, 0.2, 1.0);\n" // Orange color
    "}\n";

    // Compile shaders
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    GLint success;
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
    char infoLog[512];
    glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
    printf("Vertex Shader Compilation Failed: %s\n", infoLog);
    return false;
    }

    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
    char infoLog[512];
    glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
    printf("Fragment Shader Compilation Failed: %s\n", infoLog);
    return false;
    }

    cube->shaderProgram = glCreateProgram();
    glAttachShader(cube->shaderProgram, vertexShader);
    glAttachShader(cube->shaderProgram, fragmentShader);
    glLinkProgram(cube->shaderProgram);
    glGetProgramiv(cube->shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
    char infoLog[512];
    glGetProgramInfoLog(cube->shaderProgram, 512, NULL, infoLog);
    printf("Shader Program Linking Failed: %s\n", infoLog);
    return false;
    }
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // Setup VAO, VBO, EBO
    glGenVertexArrays(1, &cube->vao);
    glGenBuffers(1, &cube->vbo);
    glGenBuffers(1, &cube->ebo);

    glBindVertexArray(cube->vao);

    glBindBuffer(GL_ARRAY_BUFFER, cube->vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), cubeVertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cube->ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    glBindVertexArray(0);

    cube->indexCount = sizeof(cubeIndices) / sizeof(cubeIndices[0]);
    return true;
    }

    void update_transform_system(ecs_iter_t *it) {
    Transform3D *transforms = ecs_field(it, Transform3D, 0);
    for (int i = 0; i < it->count; i++) {
    if (!transforms[i].isDirty) continue;
    mat4 local;
    glm_mat4_identity(local);
    glm_scale(local, transforms[i].scale);
    glm_rotate_x(local, glm_rad(transforms[i].rotation[0]), local);
    glm_rotate_y(local, glm_rad(transforms[i].rotation[1]), local);
    glm_rotate_z(local, glm_rad(transforms[i].rotation[2]), local);
    glm_translate(local, transforms[i].position);
    glm_mat4_copy(local, transforms[i].local);
    glm_mat4_copy(local, transforms[i].world); // No parent transform
    transforms[i].isDirty = false;
    printf("Updated transform %d: Pos=(%.2f, %.2f, %.2f)\n",
    i, transforms[i].position[0], transforms[i].position[1], transforms[i].position[2]);
    }
    }

    // void update_transform_system(ecs_iter_t *it) {
    // Transform3D *transforms = ecs_field(it, Transform3D, 0);
    // for (int i = 0; i < it->count; i++) {
    // if (!transforms[i].isDirty) continue;
    // // Calculate local matrix
    // mat4 local;
    // glm_mat4_identity(local);
    // // Apply scale
    // glm_scale(local, transforms[i].scale);
    // // Apply rotation (Euler angles in degrees)
    // glm_rotate_x(local, glm_rad(transforms[i].rotation[0]), local);
    // glm_rotate_y(local, glm_rad(transforms[i].rotation[1]), local);
    // glm_rotate_z(local, glm_rad(transforms[i].rotation[2]), local);
    // // Apply translation
    // glm_translate(local, transforms[i].position);
    // glm_mat4_copy(local, transforms[i].local);
    // // Check for parent transform
    // ecs_entity_t parent = ecs_get_parent(it->world, it->entities[i]);
    // if (parent && ecs_has(it->world, parent, Transform3D)) {
    // Transform3D *parent_transform = ecs_get_mut(it->world, parent, Transform3D);
    // mat4 world;
    // glm_mat4_mul(parent_transform->world, local, world);
    // glm_mat4_copy(world, transforms[i].world);
    // } else {
    // glm_mat4_copy(local, transforms[i].world);
    // }
    // transforms[i].isDirty = false;
    // }
    // }



    void render_3d_cube_system(ecs_iter_t *it) {
    Transform3D *transforms = ecs_field(it, Transform3D, 0);
    CubeContext *cube = (CubeContext *)ecs_get_ctx(it->world);

    if (!cube) {
    printf("CubeContext is NULL in render_3d_cube_system!\n");
    return;
    }

    printf("Rendering %d cubes\n", it->count);
    if (it->count == 0) {
    printf("No entities with Transform3D found!\n");
    return;
    }

    glUseProgram(cube->shaderProgram);

    // Ensure depth testing is enabled
    glEnable(GL_DEPTH_TEST);

    // Setup view and projection matrices
    mat4 view, projection;
    glm_mat4_identity(view);
    glm_lookat((vec3){0.0f, 0.0f, 5.0f}, (vec3){0.0f, 0.0f, 0.0f}, (vec3){0.0f, 1.0f, 0.0f}, view);

    int ww, hh;
    SDL_GetWindowSize(SDL_GL_GetCurrentWindow(), &ww, &hh);
    glm_perspective(glm_rad(45.0f), (float)ww / hh, 0.1f, 100.0f, projection);

    GLint modelLoc = glGetUniformLocation(cube->shaderProgram, "model");
    GLint viewLoc = glGetUniformLocation(cube->shaderProgram, "view");
    GLint projLoc = glGetUniformLocation(cube->shaderProgram, "projection");

    glUniformMatrix4fv(viewLoc, 1, GL_FALSE, (float*)view);
    glUniformMatrix4fv(projLoc, 1, GL_FALSE, (float*)projection);

    glBindVertexArray(cube->vao);
    for (int i = 0; i < it->count; i++) {
    printf("Rendering cube %d: Pos=(%.2f, %.2f, %.2f), Rot=(%.2f, %.2f, %.2f), Scale=(%.2f, %.2f, %.2f)\n",
    i, transforms[i].position[0], transforms[i].position[1], transforms[i].position[2],
    transforms[i].rotation[0], transforms[i].rotation[1], transforms[i].rotation[2],
    transforms[i].scale[0], transforms[i].scale[1], transforms[i].scale[2]);
    glUniformMatrix4fv(modelLoc, 1, GL_FALSE, (float*)transforms[i].world);
    glDrawElements(GL_TRIANGLES, cube->indexCount, GL_UNSIGNED_INT, 0);
    }
    glBindVertexArray(0);

    // Check for OpenGL errors
    GLenum err;
    while ((err = glGetError()) != GL_NO_ERROR) {
    printf("OpenGL Error in render_3d_cube_system: %u\n", err);
    }
    }



    // nope error on attach child
    // void start_up_system(ecs_iter_t *it) {
    // printf("start up\n");
    // // Create parent cube
    // ecs_entity_t parent = ecs_entity(it->world, { .name = "ParentCube" });
    // ecs_set(it->world, parent, Transform3D, {
    // .position = {0.0f, 0.0f, 0.0f},
    // .rotation = {0.0f, 0.0f, 0.0f},
    // .scale = {1.0f, 1.0f, 1.0f},
    // .isDirty = true
    // });
    // // Create child cube
    // ecs_entity_t child = ecs_entity(it->world, { .name = "ChildCube" });
    // ecs_set(it->world, child, Transform3D, {
    // .position = {2.0f, 0.0f, 0.0f}, // Offset from parent
    // .rotation = {0.0f, 45.0f, 0.0f},
    // .scale = {0.5f, 0.5f, 0.5f},
    // .isDirty = true
    // });
    // ecs_add_pair(it->world, child, EcsChildOf, parent);
    // // Initialize cube mesh
    // CubeContext* cube = malloc(sizeof(CubeContext));
    // if (!init_cube_mesh(cube)) {
    // printf("Failed to initialize cube mesh\n");
    // return;
    // }
    // ecs_set_ctx(it->world, cube, NULL);
    // }



    int main() {
    if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) {
    fprintf(stderr, "Failed to init video! %s\n", SDL_GetError());
    return 1;
    }

    float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
    SDL_WindowFlags window_flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_OPENGL;
    SDL_Window* window = SDL_CreateWindow("SDL3 OpenGL Font Tranformer 3d", (int)(1280 * main_scale), (int)(720 * main_scale), window_flags);
    if (!window) {
    printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError());
    SDL_Quit();
    return -1;
    }
    SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
    SDL_ShowWindow(window);

    // Create OpenGL context
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
    SDL_GLContext gl_context = SDL_GL_CreateContext(window);
    if (!gl_context) {
    printf("Error: SDL_GL_CreateContext(): %s\n", SDL_GetError());
    SDL_DestroyWindow(window);
    SDL_Quit();
    return -1;
    }
    SDL_GL_MakeCurrent(window, gl_context);

    int version = gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress);
    if (version == 0) {
    printf("Failed to initialize GLAD\n");
    SDL_GL_DestroyContext(gl_context);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 1;
    }
    printf("OpenGL loaded: version %s\n", glGetString(GL_VERSION));

    FontData *font_data = NULL;
    if (!init_font("resources/Kenney Mini.ttf", 32.0f, main_scale, &font_data)) {
    SDL_GL_DestroyContext(gl_context);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return -1;
    }

    // Initialize shaders and buffers
    GLuint program, vao, vbo;
    if (!init_font_shaders_and_buffers(&program, &vao, &vbo)) {
    // cleanup_font(&font_data);
    cleanup_font(font_data);
    SDL_GL_DestroyContext(gl_context);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return -1;
    }

    // Setup Dear ImGui context
    igCreateContext(NULL);
    ImGuiIO* io = igGetIO();
    io->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
    io->ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;

    // Setup Dear ImGui style
    igStyleColorsDark(NULL);
    ImGuiStyle* style = igGetStyle();
    ImGuiStyle_ScaleAllSizes(style, main_scale);

    // Setup Platform/Renderer backends
    ImGui_ImplSDL3_InitForOpenGL(window, gl_context);
    ImGui_ImplOpenGL3_Init("#version 330");

    // Our state
    bool show_demo_window = true;
    bool show_another_window = false;

    float clear_colorE4[4] = {0.45f, 0.55f, 0.60f, 1.00f};

    ImVec4 clearColorE3;
    clearColorE3.x = 0.45f;
    clearColorE3.y = 0.55f;
    clearColorE3.z = 0.60f;
    clearColorE3.w = 1.00f;

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    float clear_color[4] = {0.45f, 0.55f, 0.60f, 1.00f};
    bool done = false;
    const char* text = "Hello World! Glad 2.0.8";


    ecs_world_t *world = ecs_init();
    CubeContext* cube;

    ECS_COMPONENT(world, Position);
    ECS_COMPONENT(world, Velocity);
    // ECS_COMPONENT(world, CubeContext);
    ECS_COMPONENT(world, Transform3D);

    // EcsOnStart
    // EcsPreUpdate
    // EcsOnUpdate
    // EcsPostUpdate


    // start up system
    // ECS_SYSTEM(world, start_up_system, EcsOnStart);
    ECS_SYSTEM(world, update_transform_system, EcsPreUpdate, Transform3D);
    //render 3d cube
    ECS_SYSTEM(world, render_3d_cube_system, EcsOnUpdate, Transform3D);


    // Create parent cube
    ecs_entity_t parent = ecs_entity(world, { .name = "ParentCube" });
    ecs_set(world, parent, Transform3D, {
    .position = {0.0f, 0.0f, 0.0f},
    .rotation = {0.0f, 0.0f, 0.0f},
    .scale = {1.0f, 1.0f, 1.0f},
    .isDirty = true
    });

    // Create child cube
    ecs_entity_t child = ecs_entity(world, { .name = "ChildCube" });
    ecs_set(world, child, Transform3D, {
    // .position = {2.0f, 0.0f, 0.0f}, // Offset from parent
    .position = {0.0f, 0.0f, 0.0f}, // Offset from parent
    // .rotation = {0.0f, 0.0f, 0.0f},
    .rotation = {0.0f, 45.0f, 0.0f},
    .scale = {1.0f, 1.0f, 1.0f},
    .isDirty = true
    });
    ecs_add_pair(world, child, EcsChildOf, parent);

    // Initialize cube mesh
    cube = malloc(sizeof(CubeContext));
    if (!init_cube_mesh(cube)) {
    printf("Failed to initialize cube mesh\n");
    // return;
    }
    ecs_set_ctx(world, cube, NULL);


    // Do the ECS stuff
    ecs_entity_t e = ecs_entity(world, { .name = "Bob" });
    printf("Entity name: %s\n", ecs_get_name(world, e));



    // glEnable(GL_DEPTH_TEST);

    while (!done) {
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
    ImGui_ImplSDL3_ProcessEvent(&event);
    if (event.type == SDL_EVENT_QUIT)
    done = true;
    if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window))
    done = true;
    }

    if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) {
    SDL_Delay(10);
    continue;
    }

    // Start the Dear ImGui frame
    ImGui_ImplOpenGL3_NewFrame();
    ImGui_ImplSDL3_NewFrame();
    igNewFrame();

    // Simple window
    {
    static float f = 0.0f;
    static int counter = 0;
    // igBegin("Hello, world!", NULL, 0);
    // igText("This is some useful text.");
    // igCheckbox("Demo Window", &show_demo_window);
    // igCheckbox("Another Window", &show_another_window);
    // igSliderFloat("float", &f, 0.0f, 1.0f, "%.3f", 0);
    // igText("Choose a color:");
    // if(igColorEdit4("clear color E4", (float*)&clear_colorE4, 0)){
    // // Log the new color values whenever they change
    // printf("Color changed to: R=%.2f, G=%.2f, B=%.2f, A=%.2f\n",
    // clear_colorE4[0], clear_colorE4[1], clear_colorE4[2], clear_colorE4[3]);
    // }
    // igColorEdit3("clear color E3", (float *)&clearColorE3, 0);

    // ImVec2 buttonSize = {0, 0};
    // if (igButton("Button", buttonSize))
    // counter++;
    // igSameLine(0.0f, -1.0f);
    // igText("counter = %d", counter);
    // igText("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io->Framerate, io->Framerate);
    // igEnd();
    }

    {
    igBegin("transform3d", NULL, 0);
    ImVec2 buttonSize = {0, 0};
    if (igButton("query", buttonSize)){

    }
    igEnd();
    }




    // Rendering
    igRender();

    int ww, hh;
    SDL_GetWindowSize(window, &ww, &hh);
    glViewport(0, 0, ww, hh);
    glViewport(0, 0, (int)io->DisplaySize.x, (int)io->DisplaySize.y); //cimgui
    glClearColor(clear_colorE4[0], clear_colorE4[1], clear_colorE4[2], clear_colorE4[3]);
    // glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]);
    // glClear(GL_COLOR_BUFFER_BIT);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // Enable depth testing for 3D rendering
    glEnable(GL_DEPTH_TEST);

    // Test cube rendering
    // {
    // printf("Test cube rendering\n");

    // glUseProgram(cube->shaderProgram);

    // // Setup view and projection matrices (same as render_3d_cube_system)
    // mat4 view, projection;
    // glm_mat4_identity(view);
    // glm_lookat((vec3){0.0f, 0.0f, 5.0f}, (vec3){0.0f, 0.0f, 0.0f}, (vec3){0.0f, 1.0f, 0.0f}, view);
    // glm_perspective(glm_rad(45.0f), (float)ww / hh, 0.1f, 100.0f, projection);

    // // Simple model matrix (e.g., translate to (0, 0, 0) with no rotation or scale)
    // mat4 model;
    // glm_mat4_identity(model);
    // // Optional: Translate to make sure it’s visible
    // glm_translate(model, (vec3){0.0f, 0.0f, 0.0f});

    // // Set uniforms
    // GLint modelLoc = glGetUniformLocation(cube->shaderProgram, "model");
    // GLint viewLoc = glGetUniformLocation(cube->shaderProgram, "view");
    // GLint projLoc = glGetUniformLocation(cube->shaderProgram, "projection");
    // glUniformMatrix4fv(modelLoc, 1, GL_FALSE, (float*)model);
    // glUniformMatrix4fv(viewLoc, 1, GL_FALSE, (float*)view);
    // glUniformMatrix4fv(projLoc, 1, GL_FALSE, (float*)projection);

    // // Draw the cube
    // glBindVertexArray(cube->vao);
    // glDrawElements(GL_TRIANGLES, cube->indexCount, GL_UNSIGNED_INT, 0);
    // glBindVertexArray(0);

    // // Check for OpenGL errors
    // GLenum err;
    // while ((err = glGetError()) != GL_NO_ERROR) {
    // printf("OpenGL Error during test render: %u\n", err);
    // }
    // }


    ecs_progress(world, 0); // run systems in default pipeline


    // Render 2D text
    // render_text(&font_data, program, vao, vbo, text, 25.0f, 150.0f, ww, hh, 1.0f, 1.0f, 1.0f, 1.0f);// test
    render_text(font_data, program, vao, vbo, "Hello, World!", 100.0f, 100.0f, ww, hh, 1.0f, 1.0f, 1.0f, 1.0f);

    ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData());
    SDL_GL_SwapWindow(window);
    }

    // Cleanup

    // CubeContext* cube = ecs_get_ctx(world);
    // cube = ecs_get_ctx(world);
    if (cube) {
    glDeleteVertexArrays(1, &cube->vao);
    glDeleteBuffers(1, &cube->vbo);
    glDeleteBuffers(1, &cube->ebo);
    glDeleteProgram(cube->shaderProgram);
    free(cube);
    }


    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplSDL3_Shutdown();
    igDestroyContext(NULL);

    cleanup_font(font_data); // conflict due cimgui modified font match data type.
    glDeleteProgram(program);
    glDeleteBuffers(1, &vbo);
    glDeleteVertexArrays(1, &vao);

    ecs_fini(world);

    SDL_GL_DestroyContext(gl_context);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
    }