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

strichmaennchen git · main

git clone https://git.christianimmanuel.de/games/strichmaennchen.gitwget https://git.christianimmanuel.de/games/strichmaennchen/archive/strichmaennchen.tar.gz
mesh.c 35.3 KB · 996 lines raw
#include "mesh.h"
#include "linalg.h"
#include "renderer.h"
#include "stdio.h"
#include "glfuncs.h"

#include <stdlib.h>

float cosf(float x);
float tanf(float x);
float sinf(float x);
float sqrtf(float x);

#define M_PI 3.14159265358979323846



const Mesh mesh_default = {
    .vao = 0,
    .vbo = 0,
    .ebo = 0,
    .vertexCount = 0,
    .drawMode = GL_TRIANGLES,
    .hasIndices = false,
};

void Mesh_draw(Mesh mesh, struct Renderer *renderer, TransformMatrices* trans_matrices, Time* time, Vec3 viewPos) {
    glUseProgram(renderer->shaderProgram);

    // Set matrices
    GLint locModel = glGetUniformLocation(renderer->shaderProgram, "model");
    GLint locView  = glGetUniformLocation(renderer->shaderProgram, "view");
    GLint locProj  = glGetUniformLocation(renderer->shaderProgram, "projection");
    glUniformMatrix4fv(locModel, 1, GL_FALSE, (float*)&(trans_matrices->model));
    glUniformMatrix4fv(locView,  1, GL_FALSE, (float*)&(trans_matrices->view));
    glUniformMatrix4fv(locProj,  1, GL_FALSE, (float*)&(trans_matrices->projection));

    // Set light & camera positions using Time
    GLint locLightDir   = glGetUniformLocation(renderer->shaderProgram, "lightDirection");
    GLint locLightColor = glGetUniformLocation(renderer->shaderProgram, "lightColor");
    GLint locViewPos    = glGetUniformLocation(renderer->shaderProgram, "viewPos");

    glUniform3f(locLightDir, time->sunDirection.x, time->sunDirection.y, time->sunDirection.z);
    glUniform3f(locLightColor, time->sunColor.x, time->sunColor.y, time->sunColor.z);
    glUniform3f(locViewPos, viewPos.x, viewPos.y, viewPos.z);

    // set dayFactor
    GLint locDay = glGetUniformLocation(renderer->shaderProgram, "dayFactor");
    if (locDay != -1) {
        glUniform1f(locDay, time->dayFactor);
    }

    // For main pass only (not shadow pass)
    Mat4 lightSpace = mat4_mul(renderer->lightProjection, mat4_mul(renderer->lightView, trans_matrices->model));
    GLint locLightSpace = glGetUniformLocation(renderer->shaderProgram, "lightSpaceMatrix");
    glUniformMatrix4fv(locLightSpace, 1, GL_FALSE, (float*)&lightSpace);

    // Bind shadow map
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, renderer->shadowMap);
    GLint locShadowMap = glGetUniformLocation(renderer->shaderProgram, "shadowMap");
    glUniform1i(locShadowMap, 0);

    // Draw
    glBindVertexArray(mesh.vao);
    if (mesh.hasIndices)
        glDrawElements(GL_TRIANGLES, mesh.indexCount, GL_UNSIGNED_INT, 0);
    else
        glDrawArrays(mesh.drawMode, 0, mesh.vertexCount);
    glBindVertexArray(0);
}

Mesh Mesh_createTriangle() {
    float vertices[] = {
        // positions       // colors
        -0.5f, 0.0f, 0.0f,   1.0f, 0.0f, 0.0f,
         0.5f, 0.0f, 0.0f,   0.0f, 1.0f, 0.0f,
         0.0f, 1.0f, 0.0f,   0.0f, 0.0f, 1.0f
    };

    Mesh m = mesh_default;

    glGenVertexArrays(1, &m.vao);
    glGenBuffers(1, &m.vbo);

    glBindVertexArray(m.vao);
    glBindBuffer(GL_ARRAY_BUFFER, m.vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    // color attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    glBindVertexArray(0);

    m.vertexCount = 3;
    m.drawMode = GL_TRIANGLES;
    m.ebo = 0;
    return m;
}

Mesh Mesh_createPyramid(float size, float height) {
    float half = size / 2.0f;

    float vertices[] = {
        // positions              // colors
        // Base (square)
        -half, 0.0f, -half,       1,0,0,   // 0
         half, 0.0f, -half,       0,1,0,   // 1
         half, 0.0f,  half,       0,0,1,   // 2
        -half, 0.0f,  half,       1,1,0,   // 3

        // Apex
         0.0f, height, 0.0f,      1,0,1    // 4
    };

    unsigned int indices[] = {
        // Base
        0,1,2,
        2,3,0,
        // Sides
        0,1,4,
        1,2,4,
        2,3,4,
        3,0,4
    };

    Mesh m = mesh_default;
    m.vertexCount = sizeof(vertices)/sizeof(vertices[0]);
    m.indexCount = sizeof(indices)/sizeof(indices[0]);
    m.drawMode = GL_TRIANGLES;
    m.hasIndices = true;

    glGenVertexArrays(1, &m.vao);
    glGenBuffers(1, &m.vbo);
    glGenBuffers(1, &m.ebo);

    glBindVertexArray(m.vao);

    glBindBuffer(GL_ARRAY_BUFFER, m.vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

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

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

    glBindVertexArray(0);

    return m;
}


Mesh Mesh_createCube(Vec3 size, VertexPreset preset) {
    float hx = size.x/2.0f;
    float hy = size.y/2.0f;
    float hz = size.z/2.0f;

    float vertices[24*9]; // 24 vertices, 3 pos + 3 normal + 3 color

    // Face normals
    Vec3 faceNormals[6] = {
        { 0,  0, -1}, // back
        { 0,  0,  1}, // front
        {-1,  0,  0}, // left
        { 1,  0,  0}, // right
        { 0, -1,  0}, // bottom
        { 0,  1,  0}  // top
    };

    // Face colors (same as before)
    float faceColors[6][3];
    switch (preset) {
        case CUBE_VERTICES_STANDARD:
            for (int i=0;i<6;i++){ faceColors[i][0]=1; faceColors[i][1]=1; faceColors[i][2]=1; }
            break;
        case CUBE_VERTICES_MODERN:
            faceColors[0][0]=1;faceColors[0][1]=0;faceColors[0][2]=0;
            faceColors[1][0]=0;faceColors[1][1]=1;faceColors[1][2]=0;
            faceColors[2][0]=0;faceColors[2][1]=0;faceColors[2][2]=1;
            faceColors[3][0]=1;faceColors[3][1]=1;faceColors[3][2]=0;
            faceColors[4][0]=1;faceColors[4][1]=0;faceColors[4][2]=1;
            faceColors[5][0]=0;faceColors[5][1]=1;faceColors[5][2]=1;
            break;
        case CUBE_VERTICES_GROUND:
            for (int i=0;i<4;i++){ faceColors[i][0]=0.55f; faceColors[i][1]=0.27f; faceColors[i][2]=0.07f; }
            faceColors[4][0]=0.3f; faceColors[4][1]=0.15f; faceColors[4][2]=0.05f;
            faceColors[5][0]=0.1f; faceColors[5][1]=0.8f; faceColors[5][2]=0.1f;
            break;
        case CUBE_VERTICES_WARM:
            for (int i=0;i<6;i++){ faceColors[i][0]=1; faceColors[i][1]=0.5; faceColors[i][2]=0; }
            break;
    }

    int vi = 0;
    Vec3 faceOffsets[6][4] = {
        {{-hx,-hy,-hz},{hx,-hy,-hz},{hx,hy,-hz},{-hx,hy,-hz}}, // back
        {{-hx,-hy,hz},{hx,-hy,hz},{hx,hy,hz},{-hx,hy,hz}},     // front
        {{-hx,-hy,-hz},{-hx,hy,-hz},{-hx,hy,hz},{-hx,-hy,hz}}, // left
        {{hx,-hy,-hz},{hx,hy,-hz},{hx,hy,hz},{hx,-hy,hz}},     // right
        {{-hx,-hy,-hz},{hx,-hy,-hz},{hx,-hy,hz},{-hx,-hy,hz}}, // bottom
        {{-hx,hy,-hz},{hx,hy,-hz},{hx,hy,hz},{-hx,hy,hz}},     // top
    };



    for (int f=0; f<6; f++) {
        for (int v=0; v<4; v++) {
            vertices[vi++] = faceOffsets[f][v].x;
            vertices[vi++] = faceOffsets[f][v].y;
            vertices[vi++] = faceOffsets[f][v].z;

            vertices[vi++] = faceNormals[f].x;
            vertices[vi++] = faceNormals[f].y;
            vertices[vi++] = faceNormals[f].z;

            vertices[vi++] = faceColors[f][0];
            vertices[vi++] = faceColors[f][1];
            vertices[vi++] = faceColors[f][2];
        }
    }

    unsigned int indices[] = {
        0,1,2, 2,3,0,    // back
        4,5,6, 6,7,4,    // front
        8,9,10,10,11,8,  // left
        12,13,14,14,15,12,// right
        16,17,18,18,19,16,// bottom
        20,21,22,22,23,20 // top
    };

    Mesh m = mesh_default;
    m.vertexCount = 24;
    m.indexCount = 36;
    m.hasIndices = true;
    m.drawMode = GL_TRIANGLES;

    glGenVertexArrays(1,&m.vao);
    glGenBuffers(1,&m.vbo);
    glGenBuffers(1,&m.ebo);

    glBindVertexArray(m.vao);
    glBindBuffer(GL_ARRAY_BUFFER,m.vbo);
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m.ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);

    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)0);       // pos
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)(3*sizeof(float))); // normal
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)(6*sizeof(float))); // color
    glEnableVertexAttribArray(2);
    glBindVertexArray(0);

    return m;
}




Mesh Mesh_createTree(float trunkHeight, float trunkRadius, int leafLayers, float leafRadius) {
    Mesh m = mesh_default;

    const int segments = 12; // circular segments for trunk and leaves
    int vOffset = 0;

    int floatsPerVertex = 9;
    int maxVertices = segments*36 + leafLayers*segments*18; // old
    maxVertices = maxVertices / 6 * floatsPerVertex;        // fix: scale up to 9 floats per vertex
    float* vertices = malloc(sizeof(float) * maxVertices);


    if (!vertices) {
        fprintf(stderr, "Failed to allocate memory for tree vertices\n");
        return mesh_default;
    }

    // Trunk (cylinder)
    for (int i=0; i<segments; i++) {
        float angle0 = 2*M_PI*i/segments;
        float angle1 = 2*M_PI*((i+1)%segments)/segments;

        float x0 = cosf(angle0)*trunkRadius;
        float z0 = sinf(angle0)*trunkRadius;
        float x1 = cosf(angle1)*trunkRadius;
        float z1 = sinf(angle1)*trunkRadius;

        float y0 = 0;
        float y1 = trunkHeight;

        // side quad as two triangles
        // triangle 1
        // Normal is just outward direction on XZ plane
        float nx0 = cosf(angle0);
        float nz0 = sinf(angle0);
        float nx1 = cosf(angle1);
        float nz1 = sinf(angle1);

        // V1
        vertices[vOffset++] = x0; vertices[vOffset++] = y0; vertices[vOffset++] = z0;
        vertices[vOffset++] = nx0; vertices[vOffset++] = 0.0f; vertices[vOffset++] = nz0;
        vertices[vOffset++] = 0.55f; vertices[vOffset++] = 0.27f; vertices[vOffset++] = 0.07f;

        // V2
        vertices[vOffset++] = x1; vertices[vOffset++] = y0; vertices[vOffset++] = z1;
        vertices[vOffset++] = nx1; vertices[vOffset++] = 0.0f; vertices[vOffset++] = nz1;
        vertices[vOffset++] = 0.55f; vertices[vOffset++] = 0.27f; vertices[vOffset++] = 0.07f;

        // V3
        vertices[vOffset++] = x1; vertices[vOffset++] = y1; vertices[vOffset++] = z1;
        vertices[vOffset++] = nx1; vertices[vOffset++] = 0.0f; vertices[vOffset++] = nz1;
        vertices[vOffset++] = 0.55f; vertices[vOffset++] = 0.27f; vertices[vOffset++] = 0.07f;

        // Second triangle (x0,y0,z0) -> (x1,y1,z1) -> (x0,y1,z0)

        // V4
        vertices[vOffset++] = x0; vertices[vOffset++] = y0; vertices[vOffset++] = z0;
        vertices[vOffset++] = nx0; vertices[vOffset++] = 0.0f; vertices[vOffset++] = nz0;
        vertices[vOffset++] = 0.55f; vertices[vOffset++] = 0.27f; vertices[vOffset++] = 0.07f;

        // V5
        vertices[vOffset++] = x1; vertices[vOffset++] = y1; vertices[vOffset++] = z1;
        vertices[vOffset++] = nx1; vertices[vOffset++] = 0.0f; vertices[vOffset++] = nz1;
        vertices[vOffset++] = 0.55f; vertices[vOffset++] = 0.27f; vertices[vOffset++] = 0.07f;

        // V6
        vertices[vOffset++] = x0; vertices[vOffset++] = y1; vertices[vOffset++] = z0;
        vertices[vOffset++] = nx0; vertices[vOffset++] = 0.0f; vertices[vOffset++] = nz0;
        vertices[vOffset++] = 0.55f; vertices[vOffset++] = 0.27f; vertices[vOffset++] = 0.07f;

    }

    // Leaves (cones, layered from top to bottom)
    for (int l=0; l<leafLayers; l++) {
        float yLayer = trunkHeight + l*0.6f; // staggered layers
        float rLayer = leafRadius * (1.0f - (float)l/leafLayers);

        for (int i=0; i<segments; i++) {
            float angle0 = 2*M_PI*i/segments;
            float angle1 = 2*M_PI*((i+1)%segments)/segments;

            float x0 = cosf(angle0)*rLayer;
            float z0 = sinf(angle0)*rLayer;
            float x1 = cosf(angle1)*rLayer;
            float z1 = sinf(angle1)*rLayer;

            // top of the cone
            float tx = 0.0f, ty = yLayer + 0.8f, tz = 0.0f;

            // compute normal via cross product of two triangle edges
            float ux = x0 - tx;
            float uy = yLayer - ty;
            float uz = z0 - tz;

            float vx = x1 - tx;
            float vy = yLayer - ty;
            float vz = z1 - tz;

            // correct cross product: v x u instead of u x v
            float nx = vy*uz - vz*uy;
            float ny = vz*ux - vx*uz;
            float nz = vx*uy - vy*ux;

            // normalize
            float len = sqrtf(nx*nx + ny*ny + nz*nz);
            if (len > 0.0f) {
                nx /= len; ny /= len; nz /= len;
            }

            // green color
            float cr = 0.0f, cg = 0.8f, cb = 0.0f;

            // V1: tip of cone
            vertices[vOffset++] = tx; vertices[vOffset++] = ty; vertices[vOffset++] = tz;
            vertices[vOffset++] = nx; vertices[vOffset++] = ny; vertices[vOffset++] = nz;
            vertices[vOffset++] = cr; vertices[vOffset++] = cg; vertices[vOffset++] = cb;

            // V2: base corner 0
            vertices[vOffset++] = x0; vertices[vOffset++] = yLayer; vertices[vOffset++] = z0;
            vertices[vOffset++] = nx; vertices[vOffset++] = ny; vertices[vOffset++] = nz;
            vertices[vOffset++] = cr; vertices[vOffset++] = cg; vertices[vOffset++] = cb;

            // V3: base corner 1
            vertices[vOffset++] = x1; vertices[vOffset++] = yLayer; vertices[vOffset++] = z1;
            vertices[vOffset++] = nx; vertices[vOffset++] = ny; vertices[vOffset++] = nz;
            vertices[vOffset++] = cr; vertices[vOffset++] = cg; vertices[vOffset++] = cb;
        }
    }
    // Generate OpenGL buffers
    glGenVertexArrays(1, &m.vao);
    glGenBuffers(1, &m.vbo);

    glBindVertexArray(m.vao);
    glBindBuffer(GL_ARRAY_BUFFER, m.vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*vOffset, vertices, GL_STATIC_DRAW);


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

    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 9*sizeof(float), (void*)(3*sizeof(float))); // normal
    glEnableVertexAttribArray(1);

    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 9*sizeof(float), (void*)(6*sizeof(float))); // color
    glEnableVertexAttribArray(2);



    glBindVertexArray(0);

    m.vertexCount = vOffset / 9;
    m.drawMode = GL_TRIANGLES;
    m.ebo = 0;
    m.hasIndices = false;

    free(vertices);
    return m;
}

Mesh Mesh_createCylinder(float diameter, float height, int segments, VertexPreset preset) {
    float radius = diameter*0.5f;
    float hy = height * 0.5f;

    // vertex layout: pos(3), normal(3), color(3) = 9 floats
    int side_vertices = segments * 2;        // top + bottom ring
    int cap_ring_vertices = segments * 2;    // cap ring (top + bottom)
    int cap_center_vertices = 2;             // top + bottom centers
    int vertex_count = side_vertices + cap_ring_vertices + cap_center_vertices;

    float *vertices = malloc(vertex_count * 9 * sizeof(float));

    // color setup
    float color[3] = {1,1,1};
    switch (preset) {
        case CUBE_VERTICES_STANDARD: color[0]=color[1]=color[2]=1; break;
        case CUBE_VERTICES_MODERN:   color[0]=1; color[1]=0; color[2]=0; break;
        case CUBE_VERTICES_GROUND:   color[0]=0.55f; color[1]=0.27f; color[2]=0.07f; break;
        case CUBE_VERTICES_WARM:     color[0]=1; color[1]=0.5f; color[2]=0; break;
    }

    // precompute sin/cos
    float *cos_vals = malloc(segments * sizeof(float));
    float *sin_vals = malloc(segments * sizeof(float));
    for (int i=0; i<segments; i++) {
        float theta = (2.0f * M_PI * i) / segments;
        cos_vals[i] = cosf(theta);
        sin_vals[i] = sinf(theta);
    }

    int vi = 0;

    // side vertices
    for (int i=0; i<segments; i++) {
        float x = cos_vals[i] * radius;
        float z = sin_vals[i] * radius;

        // bottom
        vertices[vi++] = x; vertices[vi++] = -hy; vertices[vi++] = z;
        vertices[vi++] = cos_vals[i]; vertices[vi++] = 0; vertices[vi++] = sin_vals[i];
        vertices[vi++] = color[0]; vertices[vi++] = color[1]; vertices[vi++] = color[2];

        // top
        vertices[vi++] = x; vertices[vi++] =  hy; vertices[vi++] = z;
        vertices[vi++] = cos_vals[i]; vertices[vi++] = 0; vertices[vi++] = sin_vals[i];
        vertices[vi++] = color[0]; vertices[vi++] = color[1]; vertices[vi++] = color[2];
    }

    // cap ring vertices
    for (int i=0; i<segments; i++) {
        float x = cos_vals[i] * radius;
        float z = sin_vals[i] * radius;

        // bottom cap ring
        vertices[vi++] = x; vertices[vi++] = -hy; vertices[vi++] = z;
        vertices[vi++] = 0; vertices[vi++] = -1; vertices[vi++] = 0;
        vertices[vi++] = color[0]; vertices[vi++] = color[1]; vertices[vi++] = color[2];

        // top cap ring
        vertices[vi++] = x; vertices[vi++] =  hy; vertices[vi++] = z;
        vertices[vi++] = 0; vertices[vi++] =  1; vertices[vi++] = 0;
        vertices[vi++] = color[0]; vertices[vi++] = color[1]; vertices[vi++] = color[2];
    }

    // cap centers
    // bottom center
    vertices[vi++] = 0; vertices[vi++] = -hy; vertices[vi++] = 0;
    vertices[vi++] = 0; vertices[vi++] = -1; vertices[vi++] = 0;
    vertices[vi++] = color[0]; vertices[vi++] = color[1]; vertices[vi++] = color[2];

    // top center
    vertices[vi++] = 0; vertices[vi++] =  hy; vertices[vi++] = 0;
    vertices[vi++] = 0; vertices[vi++] =  1; vertices[vi++] = 0;
    vertices[vi++] = color[0]; vertices[vi++] = color[1]; vertices[vi++] = color[2];

    // indices
    int side_indices = segments * 6;
    int cap_indices  = segments * 6;
    int index_count  = side_indices + cap_indices;
    unsigned int *indices = malloc(index_count * sizeof(unsigned int));

    int ii = 0;
    // sides
    for (int i=0; i<segments; i++) {
        int i0 = i*2;
        int i1 = i0 + 1;
        int i2 = (i0 + 2) % (segments*2);
        int i3 = (i0 + 3) % (segments*2);

        indices[ii++] = i0; indices[ii++] = i2; indices[ii++] = i1;
        indices[ii++] = i1; indices[ii++] = i2; indices[ii++] = i3;
    }

    int cap_base = side_vertices;
    int center_bottom = side_vertices + cap_ring_vertices;
    int center_top    = center_bottom + 1;

    for (int i=0; i<segments; i++) {
        int next = (i+1) % segments;

        // bottom cap
        indices[ii++] = center_bottom;
        indices[ii++] = cap_base + i*2;
        indices[ii++] = cap_base + next*2;

        // top cap
        indices[ii++] = center_top;
        indices[ii++] = cap_base + next*2+1;
        indices[ii++] = cap_base + i*2+1;
    }

    Mesh m = mesh_default;
    m.vertexCount = vertex_count;
    m.indexCount = index_count;
    m.hasIndices = true;
    m.drawMode = GL_TRIANGLES;

    glGenVertexArrays(1,&m.vao);
    glGenBuffers(1,&m.vbo);
    glGenBuffers(1,&m.ebo);

    glBindVertexArray(m.vao);
    glBindBuffer(GL_ARRAY_BUFFER,m.vbo);
    glBufferData(GL_ARRAY_BUFFER,vertex_count*9*sizeof(float),vertices,GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m.ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,index_count*sizeof(unsigned int),indices,GL_STATIC_DRAW);

    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)(3*sizeof(float)));
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)(6*sizeof(float)));
    glEnableVertexAttribArray(2);
    glBindVertexArray(0);

    free(vertices);
    free(indices);
    free(cos_vals);
    free(sin_vals);

    return m;
}

Mesh Mesh_createAxes(float length) {
    float vertices[] = {
        // X axis (red)
        0.0f, 0.0f, 0.0f,  1,0,0,
        length, 0.0f, 0.0f, 1,0,0,
        // Y axis (green)
        0.0f, 0.0f, 0.0f,  0,1,0,
        0.0f, length, 0.0f, 0,1,0,
        // Z axis (blue)
        0.0f, 0.0f, 0.0f,  0,0,1,
        0.0f, 0.0f, length,0,0,1
    };

    Mesh m = mesh_default;
    glGenVertexArrays(1, &m.vao);
    glGenBuffers(1, &m.vbo);

    glBindVertexArray(m.vao);
    glBindBuffer(GL_ARRAY_BUFFER, m.vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

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

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

    glBindVertexArray(0);

    m.vertexCount = 6;   // 3 lines * 2 vertices
    m.drawMode = GL_LINES;
    m.ebo = 0;
    m.hasIndices = false;

    return m;
}

static void addCube(
    float* vertices, unsigned int* indices,
    int* vOffset, int* iOffset, int* vertexBase,
    Vec3 pos, Vec3 size, VertexPreset preset)
{
    float hx = size.x / 2.0f;
    float hy = size.y / 2.0f;
    float hz = size.z / 2.0f;

    float faceColors[6][3];
    switch (preset) {
        case CUBE_VERTICES_STANDARD:
            for (int f=0; f<6; f++){ faceColors[f][0]=1; faceColors[f][1]=1; faceColors[f][2]=1; }
            break;
        case CUBE_VERTICES_MODERN:
            faceColors[0][0]=1;faceColors[0][1]=0;faceColors[0][2]=0;
            faceColors[1][0]=0;faceColors[1][1]=1;faceColors[1][2]=0;
            faceColors[2][0]=0;faceColors[2][1]=0;faceColors[2][2]=1;
            faceColors[3][0]=1;faceColors[3][1]=1;faceColors[3][2]=0;
            faceColors[4][0]=1;faceColors[4][1]=0;faceColors[4][2]=1;
            faceColors[5][0]=0;faceColors[5][1]=1;faceColors[5][2]=1;
            break;
        case CUBE_VERTICES_GROUND:
            for (int i=0;i<4;i++){ faceColors[i][0]=0.55f; faceColors[i][1]=0.27f; faceColors[i][2]=0.07f; }
            faceColors[4][0]=0.3f; faceColors[4][1]=0.15f; faceColors[4][2]=0.05f;
            faceColors[5][0]=0.1f; faceColors[5][1]=0.8f; faceColors[5][2]=0.1f;
            break;
        case CUBE_VERTICES_WARM:
            for (int f=0; f<6; f++){ faceColors[f][0]=1; faceColors[f][1]=0.5; faceColors[f][2]=0; }
            break;
    }

    Vec3 faceNormals[6] = {
        {0,0,-1}, {0,0,1}, {-1,0,0}, {1,0,0}, {0,-1,0}, {0,1,0}
    };

    Vec3 faceOffsets[6][4] = {
        {{-hx,-hy,-hz},{hx,-hy,-hz},{hx,hy,-hz},{-hx,hy,-hz}}, // back
        {{-hx,-hy,hz},{hx,-hy,hz},{hx,hy,hz},{-hx,hy,hz}},     // front
        {{-hx,-hy,-hz},{-hx,hy,-hz},{-hx,hy,hz},{-hx,-hy,hz}}, // left
        {{hx,-hy,-hz},{hx,hy,-hz},{hx,hy,hz},{hx,-hy,hz}},     // right
        {{-hx,-hy,-hz},{hx,-hy,-hz},{hx,-hy,hz},{-hx,-hy,hz}}, // bottom
        {{-hx,hy,-hz},{hx,hy,-hz},{hx,hy,hz},{-hx,hy,hz}},     // top
    };

    for (int f=0; f<6; f++) {
        for (int v=0; v<4; v++) {
            vertices[(*vOffset)++] = pos.x + faceOffsets[f][v].x;
            vertices[(*vOffset)++] = pos.y + faceOffsets[f][v].y;
            vertices[(*vOffset)++] = pos.z + faceOffsets[f][v].z;

            vertices[(*vOffset)++] = faceNormals[f].x;
            vertices[(*vOffset)++] = faceNormals[f].y;
            vertices[(*vOffset)++] = faceNormals[f].z;

            vertices[(*vOffset)++] = faceColors[f][0];
            vertices[(*vOffset)++] = faceColors[f][1];
            vertices[(*vOffset)++] = faceColors[f][2];
        }
        unsigned int base = *vertexBase + f*4;
        indices[(*iOffset)++] = base + 0;
        indices[(*iOffset)++] = base + 1;
        indices[(*iOffset)++] = base + 2;
        indices[(*iOffset)++] = base + 2;
        indices[(*iOffset)++] = base + 3;
        indices[(*iOffset)++] = base + 0;
    }
    *vertexBase += 24;
}

Mesh Mesh_createHuman(VertexPreset preset, bool is_player) {
    Mesh m = mesh_default;

    float vertices[6*24*9]; // 6 parts * 24 vertices * 9 floats (pos+normal+color)
    unsigned int indices[6*36];
    int vOffset = 0, iOffset = 0, vertexBase = 0;

    // Add body parts (same positions as before)
    addCube(vertices, indices, &vOffset, &iOffset, &vertexBase, (Vec3){0.0f, 0.285f, 0.0f}, (Vec3){0.5f,0.57f,0.25f}, preset); // torso
    if (!is_player)
        addCube(vertices, indices, &vOffset, &iOffset, &vertexBase, (Vec3){0.0f, 0.72f, 0.0f}, (Vec3){0.2f,0.2f,0.2f}, preset); // head
    addCube(vertices, indices, &vOffset, &iOffset, &vertexBase, (Vec3){-0.4f,  0.17f, 0.0f}, (Vec3){0.2f,0.76f,0.2f}, preset); // left arm
    addCube(vertices, indices, &vOffset, &iOffset, &vertexBase, (Vec3){0.4f,   0.17f, 0.0f}, (Vec3){0.2f,0.76f,0.2f}, preset); // right arm
    addCube(vertices, indices, &vOffset, &iOffset, &vertexBase, (Vec3){0.15f,  -0.4f, 0.0f}, (Vec3){0.2f,0.76f,0.2f}, preset); // left leg
    addCube(vertices, indices, &vOffset, &iOffset, &vertexBase, (Vec3){-0.15f, -0.4f, 0.0f}, (Vec3){0.2f,0.76f,0.2f}, preset); // right leg

    m.vertexCount = vertexBase;
    m.indexCount  = iOffset;
    m.hasIndices  = true;
    m.drawMode    = GL_TRIANGLES;

    glGenVertexArrays(1,&m.vao);
    glGenBuffers(1,&m.vbo);
    glGenBuffers(1,&m.ebo);

    glBindVertexArray(m.vao);
    glBindBuffer(GL_ARRAY_BUFFER,m.vbo);
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m.ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);

    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)0);        // position
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)(3*sizeof(float))); // normal
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)(6*sizeof(float))); // color
    glEnableVertexAttribArray(2);

    glBindVertexArray(0);
    return m;
}


Mesh Mesh_createHouse() {
    float length = 18.0f;
    float width  = 12.0f;
    float height = 6.0f;
    float roofHeight = 5.0f;

    float lx = length / 2.0f;
    float wy = height;
    float wz = width / 2.0f;
    float rh = roofHeight;

    // We’ll store positions, normals, colors
    // Estimate: walls (24 verts), roof (12 verts), windows+doors (4 each)
    float vertices[5000];
    unsigned int indices[5000];
    int vi = 0, ii = 0, vcount = 0;

    // Helper macro to add a vertex
    #define ADDV(px,py,pz,nx,ny,nz,r,g,b) \
        do { \
            vertices[vi++] = (px); \
            vertices[vi++] = (py); \
            vertices[vi++] = (pz); \
            vertices[vi++] = (nx); \
            vertices[vi++] = (ny); \
            vertices[vi++] = (nz); \
            vertices[vi++] = (r); \
            vertices[vi++] = (g); \
            vertices[vi++] = (b); \
        } while(0)

    // Brick color
    float br = 0.7f, bg = 0.3f, bb = 0.2f;
    // Roof color
    float rr = 0.3f, rg = 0.3f, rb = 0.3f;
    // Window color
    float wr = 0.4f, wg = 0.6f, wb = 0.9f;
    // Door color
    float dr = 0.4f, dg = 0.2f, db = 0.05f;

    // --- Walls (like cube, but long/short dimensions)
    Vec3 faceNormals[6] = {
        { 0,  0, -1}, // back
        { 0,  0,  1}, // front
        {-1,  0,  0}, // left
        { 1,  0,  0}, // right
        { 0, -1,  0}, // bottom
        { 0,  1,  0}  // top
    };

    Vec3 wallOffsets[6][4] = {
        {{-lx,0,-wz},{lx,0,-wz},{lx,wy,-wz},{-lx,wy,-wz}}, // back
        {{-lx,0,wz},{lx,0,wz},{lx,wy,wz},{-lx,wy,wz}},     // front
        {{-lx,0,-wz},{-lx,wy,-wz},{-lx,wy,wz},{-lx,0,wz}}, // left
        {{lx,0,-wz},{lx,wy,-wz},{lx,wy,wz},{lx,0,wz}},     // right
        {{-lx,0,-wz},{lx,0,-wz},{lx,0,wz},{-lx,0,wz}},     // bottom
        {{-lx,wy,-wz},{lx,wy,-wz},{lx,wy,wz},{-lx,wy,wz}}, // top
    };

    for (int f=0; f<6; f++) {
        for (int v=0; v<4; v++) {
            ADDV(wallOffsets[f][v].x, wallOffsets[f][v].y, wallOffsets[f][v].z,
                 faceNormals[f].x, faceNormals[f].y, faceNormals[f].z,
                 br,bg,bb);
        }
        indices[ii++] = vcount; indices[ii++] = vcount+1; indices[ii++] = vcount+2;
        indices[ii++] = vcount+2; indices[ii++] = vcount+3; indices[ii++] = vcount;
        vcount += 4;
    }

    // --- Roof (triangular prism)
    // ridge runs along x-axis (length), centered
    // base at y=wy, height = rh
    ADDV(-lx,wy,-wz, 0,1,0, rr,rg,rb); // left-back
    ADDV(-lx,wy, wz, 0,1,0, rr,rg,rb); // left-front
    ADDV(-lx,wy+rh, 0, 0,1,0, rr,rg,rb); // left-top
    ADDV( lx,wy,-wz, 0,1,0, rr,rg,rb); // right-back
    ADDV( lx,wy, wz, 0,1,0, rr,rg,rb); // right-front
    ADDV( lx,wy+rh, 0, 0,1,0, rr,rg,rb); // right-top

    // Left triangle
    indices[ii++] = vcount; indices[ii++] = vcount+1; indices[ii++] = vcount+2;
    // Right triangle
    indices[ii++] = vcount+3; indices[ii++] = vcount+4; indices[ii++] = vcount+5;
    // Back roof quad
    indices[ii++] = vcount; indices[ii++] = vcount+3; indices[ii++] = vcount+5;
    indices[ii++] = vcount+5; indices[ii++] = vcount+2; indices[ii++] = vcount;
    // Front roof quad
    indices[ii++] = vcount+1; indices[ii++] = vcount+4; indices[ii++] = vcount+5;
    indices[ii++] = vcount+5; indices[ii++] = vcount+2; indices[ii++] = vcount+1;
    vcount += 6;

    // --- Windows (3 along front wall at z=+wz)
    float winSize = 1.5f;
    float winY = 3.0f;
    for (int i=0;i<3;i++) {
        float cx = -lx + (i+1)*length/4.0f; // evenly spaced
        float cy = winY;
        float cz = wz+0.01f;
        ADDV(cx-winSize,cy-winSize,cz, 0,0,1, wr,wg,wb);
        ADDV(cx+winSize,cy-winSize,cz, 0,0,1, wr,wg,wb);
        ADDV(cx+winSize,cy+winSize,cz, 0,0,1, wr,wg,wb);
        ADDV(cx-winSize,cy+winSize,cz, 0,0,1, wr,wg,wb);
        indices[ii++] = vcount; indices[ii++] = vcount+1; indices[ii++] = vcount+2;
        indices[ii++] = vcount+2; indices[ii++] = vcount+3; indices[ii++] = vcount;
        vcount += 4;
    }

    // --- Doors (front and back)
    float dw = 1.0f, dh = 2.0f;
    // Front door
    ADDV(-lx+1,0,wz+0.01f, 0,0,1, dr,dg,db);
    ADDV(-lx+1+dw,0,wz+0.01f, 0,0,1, dr,dg,db);
    ADDV(-lx+1+dw,dh,wz+0.01f, 0,0,1, dr,dg,db);
    ADDV(-lx+1,dh,wz+0.01f, 0,0,1, dr,dg,db);
    indices[ii++] = vcount; indices[ii++] = vcount+1; indices[ii++] = vcount+2;
    indices[ii++] = vcount+2; indices[ii++] = vcount+3; indices[ii++] = vcount;
    vcount += 4;
    // Back door
    ADDV(lx-1-dw,0,-wz-0.01f, 0,0,-1, dr,dg,db);
    ADDV(lx-1,0,-wz-0.01f, 0,0,-1, dr,dg,db);
    ADDV(lx-1,dh,-wz-0.01f, 0,0,-1, dr,dg,db);
    ADDV(lx-1-dw,dh,-wz-0.01f, 0,0,-1, dr,dg,db);
    indices[ii++] = vcount; indices[ii++] = vcount+1; indices[ii++] = vcount+2;
    indices[ii++] = vcount+2; indices[ii++] = vcount+3; indices[ii++] = vcount;
    vcount += 4;

    // Upload to GPU
    Mesh m = mesh_default;
    m.vertexCount = vcount;
    m.indexCount = ii;
    m.hasIndices = true;
    m.drawMode = GL_TRIANGLES;

    glGenVertexArrays(1,&m.vao);
    glGenBuffers(1,&m.vbo);
    glGenBuffers(1,&m.ebo);

    glBindVertexArray(m.vao);
    glBindBuffer(GL_ARRAY_BUFFER,m.vbo);
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m.ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);

    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)(3*sizeof(float)));
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)(6*sizeof(float)));
    glEnableVertexAttribArray(2);
    glBindVertexArray(0);

    return m;
}

Mesh Mesh_createBarn() {
    float length = 24.0f;
    float width  = 12.0f;
    float height = 6.0f;
    float roofHeight = 5.0f;

    float lx = length / 2.0f;
    float wy = height;
    float wz = width / 2.0f;
    float rh = roofHeight;

    // We’ll store positions, normals, colors
    // Estimate: walls (24 verts), roof (12 verts), windows+doors (4 each)
    float vertices[5000];
    unsigned int indices[5000];
    int vi = 0, ii = 0, vcount = 0;

    // Helper macro to add a vertex
    #define ADDV(px,py,pz,nx,ny,nz,r,g,b) \
        do { \
            vertices[vi++] = (px); \
            vertices[vi++] = (py); \
            vertices[vi++] = (pz); \
            vertices[vi++] = (nx); \
            vertices[vi++] = (ny); \
            vertices[vi++] = (nz); \
            vertices[vi++] = (r); \
            vertices[vi++] = (g); \
            vertices[vi++] = (b); \
        } while(0)

    // Brick color
    float br = 0.7f, bg = 0.3f, bb = 0.2f;
    // Roof color
    float rr = 0.3f, rg = 0.3f, rb = 0.3f;

    // --- Walls (like cube, but long/short dimensions)
    Vec3 faceNormals[6] = {
        { 0,  0, -1}, // back
        { 0,  0,  1}, // front
        {-1,  0,  0}, // left
        { 1,  0,  0}, // right
        { 0, -1,  0}, // bottom
        { 0,  1,  0}  // top
    };

    Vec3 wallOffsets[6][4] = {
        {{-lx,0,-wz},{lx,0,-wz},{lx,wy,-wz},{-lx,wy,-wz}}, // back
        {{-lx,0,wz},{lx,0,wz},{lx,wy,wz},{-lx,wy,wz}},     // front
        {{-lx,0,-wz},{-lx,wy,-wz},{-lx,wy,wz},{-lx,0,wz}}, // left
        {{lx,0,-wz},{lx,wy,-wz},{lx,wy,wz},{lx,0,wz}},     // right
        {{-lx,0,-wz},{lx,0,-wz},{lx,0,wz},{-lx,0,wz}},     // bottom
        {{-lx,wy,-wz},{lx,wy,-wz},{lx,wy,wz},{-lx,wy,wz}}, // top
    };

    for (int f=0; f<6; f++) {
        for (int v=0; v<4; v++) {
            ADDV(wallOffsets[f][v].x, wallOffsets[f][v].y, wallOffsets[f][v].z,
                 faceNormals[f].x, faceNormals[f].y, faceNormals[f].z,
                 br,bg,bb);
        }
        indices[ii++] = vcount; indices[ii++] = vcount+1; indices[ii++] = vcount+2;
        indices[ii++] = vcount+2; indices[ii++] = vcount+3; indices[ii++] = vcount;
        vcount += 4;
    }

    // --- Roof (triangular prism)
    // ridge runs along x-axis (length), centered
    // base at y=wy, height = rh
    ADDV(-lx,wy,-wz, 0,1,0, rr,rg,rb); // left-back
    ADDV(-lx,wy, wz, 0,1,0, rr,rg,rb); // left-front
    ADDV(-lx,wy+rh, 0, 0,1,0, rr,rg,rb); // left-top
    ADDV( lx,wy,-wz, 0,1,0, rr,rg,rb); // right-back
    ADDV( lx,wy, wz, 0,1,0, rr,rg,rb); // right-front
    ADDV( lx,wy+rh, 0, 0,1,0, rr,rg,rb); // right-top

    // Left triangle
    indices[ii++] = vcount; indices[ii++] = vcount+1; indices[ii++] = vcount+2;
    // Right triangle
    indices[ii++] = vcount+3; indices[ii++] = vcount+4; indices[ii++] = vcount+5;
    // Back roof quad
    indices[ii++] = vcount; indices[ii++] = vcount+3; indices[ii++] = vcount+5;
    indices[ii++] = vcount+5; indices[ii++] = vcount+2; indices[ii++] = vcount;
    // Front roof quad
    indices[ii++] = vcount+1; indices[ii++] = vcount+4; indices[ii++] = vcount+5;
    indices[ii++] = vcount+5; indices[ii++] = vcount+2; indices[ii++] = vcount+1;
    vcount += 6;

    // Upload to GPU
    Mesh m = mesh_default;
    m.vertexCount = vcount;
    m.indexCount = ii;
    m.hasIndices = true;
    m.drawMode = GL_TRIANGLES;

    glGenVertexArrays(1,&m.vao);
    glGenBuffers(1,&m.vbo);
    glGenBuffers(1,&m.ebo);

    glBindVertexArray(m.vao);
    glBindBuffer(GL_ARRAY_BUFFER,m.vbo);
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m.ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);

    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)(3*sizeof(float)));
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,9*sizeof(float),(void*)(6*sizeof(float)));
    glEnableVertexAttribArray(2);
    glBindVertexArray(0);

    return m;
}